Loading packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java 0 → 100755 +254 −0 Original line number Diff line number Diff line /* * 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 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; import android.os.Handler; import android.os.Message; import android.os.SystemClock; import android.util.Slog; import android.view.MotionEvent; /** * 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 = event.copy(); } 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}", this.time, actionName(this.event.getAction()), this.event.getRawX(), this.event.getRawY() ); } } 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()) { // TODO: remove throw new RuntimeException("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.isComplete()) { mGestures.add(mCurrentGesture); } if (DEBUG) { Slog.v(TAG, String.format("Wrote %d complete gestures to %s", mLastSaveLen, mLogfile)); } } catch (IOException e) { Slog.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"); } } } packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +12 −0 Original line number Diff line number Diff line Loading @@ -79,6 +79,7 @@ import com.android.systemui.UniverseBackground; import com.android.systemui.recent.RecentTasksLoader; import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.GestureRecorder; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.NotificationData.Entry; import com.android.systemui.statusbar.RotationToggle; Loading Loading @@ -242,6 +243,9 @@ public class PhoneStatusBar extends BaseStatusBar { DisplayMetrics mDisplayMetrics = new DisplayMetrics(); // XXX: gesture research private GestureRecorder mGestureRec = new GestureRecorder("/sdcard/statusbar_gestures.dat"); private int mNavigationIconHints = 0; private final Animator.AnimatorListener mMakeIconsInvisible = new AnimatorListenerAdapter() { @Override Loading Loading @@ -1570,6 +1574,8 @@ public class PhoneStatusBar extends BaseStatusBar { } } mGestureRec.add(event); if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) { return false; } Loading Loading @@ -1604,6 +1610,7 @@ public class PhoneStatusBar extends BaseStatusBar { if (x >= edgeBorder && x < mDisplayMetrics.widthPixels - edgeBorder) { prepareTracking(y, !mExpanded);// opening if we're not already fully visible trackMovement(event); mGestureRec.tag("tracking", mExpanded ? "expanded" : "collapsed"); } } } else if (mTracking) { Loading Loading @@ -1654,6 +1661,8 @@ public class PhoneStatusBar extends BaseStatusBar { mFlingY = y; } mFlingVelocity = vel; mGestureRec.tag("fling " + ((mFlingVelocity > 0) ? "open" : "closed"), "v=" + mFlingVelocity); mHandler.post(mPerformFling); } Loading Loading @@ -1938,6 +1947,9 @@ public class PhoneStatusBar extends BaseStatusBar { } } pw.print(" status bar gestures: "); mGestureRec.dump(fd, pw, args); mNetworkController.dump(fd, pw, args); } Loading Loading
packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java 0 → 100755 +254 −0 Original line number Diff line number Diff line /* * 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 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; import android.os.Handler; import android.os.Message; import android.os.SystemClock; import android.util.Slog; import android.view.MotionEvent; /** * 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 = event.copy(); } 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}", this.time, actionName(this.event.getAction()), this.event.getRawX(), this.event.getRawY() ); } } 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()) { // TODO: remove throw new RuntimeException("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.isComplete()) { mGestures.add(mCurrentGesture); } if (DEBUG) { Slog.v(TAG, String.format("Wrote %d complete gestures to %s", mLastSaveLen, mLogfile)); } } catch (IOException e) { Slog.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"); } } }
packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +12 −0 Original line number Diff line number Diff line Loading @@ -79,6 +79,7 @@ import com.android.systemui.UniverseBackground; import com.android.systemui.recent.RecentTasksLoader; import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.GestureRecorder; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.NotificationData.Entry; import com.android.systemui.statusbar.RotationToggle; Loading Loading @@ -242,6 +243,9 @@ public class PhoneStatusBar extends BaseStatusBar { DisplayMetrics mDisplayMetrics = new DisplayMetrics(); // XXX: gesture research private GestureRecorder mGestureRec = new GestureRecorder("/sdcard/statusbar_gestures.dat"); private int mNavigationIconHints = 0; private final Animator.AnimatorListener mMakeIconsInvisible = new AnimatorListenerAdapter() { @Override Loading Loading @@ -1570,6 +1574,8 @@ public class PhoneStatusBar extends BaseStatusBar { } } mGestureRec.add(event); if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) { return false; } Loading Loading @@ -1604,6 +1610,7 @@ public class PhoneStatusBar extends BaseStatusBar { if (x >= edgeBorder && x < mDisplayMetrics.widthPixels - edgeBorder) { prepareTracking(y, !mExpanded);// opening if we're not already fully visible trackMovement(event); mGestureRec.tag("tracking", mExpanded ? "expanded" : "collapsed"); } } } else if (mTracking) { Loading Loading @@ -1654,6 +1661,8 @@ public class PhoneStatusBar extends BaseStatusBar { mFlingY = y; } mFlingVelocity = vel; mGestureRec.tag("fling " + ((mFlingVelocity > 0) ? "open" : "closed"), "v=" + mFlingVelocity); mHandler.post(mPerformFling); } Loading Loading @@ -1938,6 +1947,9 @@ public class PhoneStatusBar extends BaseStatusBar { } } pw.print(" status bar gestures: "); mGestureRec.dump(fd, pw, args); mNetworkController.dump(fd, pw, args); } Loading