Skip to content

Commit a444ee6

Browse files
dsandlerAndroid (Google) Code Review
authored andcommitted
Merge "Collecting some data on notification panel gestures."
2 parents cdb5004 + 3380534 commit a444ee6

File tree

2 files changed

+266
-0
lines changed

2 files changed

+266
-0
lines changed
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
/*
2+
* Copyright (C) 2012 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
17+
package com.android.systemui.statusbar;
18+
19+
import java.io.BufferedWriter;
20+
import java.io.FileDescriptor;
21+
import java.io.FileWriter;
22+
import java.io.IOException;
23+
import java.io.PrintWriter;
24+
import java.util.HashSet;
25+
import java.util.LinkedList;
26+
27+
import android.os.Handler;
28+
import android.os.Message;
29+
import android.os.SystemClock;
30+
import android.util.Slog;
31+
import android.view.MotionEvent;
32+
33+
/**
34+
* Convenience class for capturing gestures for later analysis.
35+
*/
36+
public class GestureRecorder {
37+
public static final boolean DEBUG = true; // for now
38+
public static final String TAG = GestureRecorder.class.getSimpleName();
39+
40+
public class Gesture {
41+
public abstract class Record {
42+
long time;
43+
public abstract String toJson();
44+
}
45+
public class MotionEventRecord extends Record {
46+
public MotionEvent event;
47+
public MotionEventRecord(long when, MotionEvent event) {
48+
this.time = when;
49+
this.event = event.copy();
50+
}
51+
String actionName(int action) {
52+
switch (action) {
53+
case MotionEvent.ACTION_DOWN:
54+
return "down";
55+
case MotionEvent.ACTION_UP:
56+
return "up";
57+
case MotionEvent.ACTION_MOVE:
58+
return "move";
59+
case MotionEvent.ACTION_CANCEL:
60+
return "cancel";
61+
default:
62+
return String.valueOf(action);
63+
}
64+
}
65+
public String toJson() {
66+
return String.format("{\"type\":\"motion\", \"time\":%d, \"action\":\"%s\", \"x\":%.2f, \"y\":%.2f}",
67+
this.time,
68+
actionName(this.event.getAction()),
69+
this.event.getRawX(),
70+
this.event.getRawY()
71+
);
72+
}
73+
}
74+
public class TagRecord extends Record {
75+
public String tag, info;
76+
public TagRecord(long when, String tag, String info) {
77+
this.time = when;
78+
this.tag = tag;
79+
this.info = info;
80+
}
81+
public String toJson() {
82+
return String.format("{\"type\":\"tag\", \"time\":%d, \"tag\":\"%s\", \"info\":\"%s\"}",
83+
this.time,
84+
this.tag,
85+
this.info
86+
);
87+
}
88+
}
89+
private LinkedList<Record> mRecords = new LinkedList<Record>();
90+
private HashSet<String> mTags = new HashSet<String>();
91+
long mDownTime = -1;
92+
boolean mComplete = false;
93+
94+
public void add(MotionEvent ev) {
95+
mRecords.add(new MotionEventRecord(ev.getEventTime(), ev));
96+
if (mDownTime < 0) {
97+
mDownTime = ev.getDownTime();
98+
} else {
99+
if (mDownTime != ev.getDownTime()) {
100+
// TODO: remove
101+
throw new RuntimeException("Assertion failure in GestureRecorder: event downTime ("
102+
+ev.getDownTime()+") does not match gesture downTime ("+mDownTime+")");
103+
}
104+
}
105+
switch (ev.getActionMasked()) {
106+
case MotionEvent.ACTION_UP:
107+
case MotionEvent.ACTION_CANCEL:
108+
mComplete = true;
109+
}
110+
}
111+
public void tag(long when, String tag, String info) {
112+
mRecords.add(new TagRecord(when, tag, info));
113+
mTags.add(tag);
114+
}
115+
public boolean isComplete() {
116+
return mComplete;
117+
}
118+
public String toJson() {
119+
StringBuilder sb = new StringBuilder();
120+
boolean first = true;
121+
sb.append("[");
122+
for (Record r : mRecords) {
123+
if (!first) sb.append(", ");
124+
first = false;
125+
sb.append(r.toJson());
126+
}
127+
sb.append("]");
128+
return sb.toString();
129+
}
130+
}
131+
132+
// -=-=-=-=-=-=-=-=-=-=-=-
133+
134+
static final long SAVE_DELAY = 5000; // ms
135+
static final int SAVE_MESSAGE = 6351;
136+
137+
private LinkedList<Gesture> mGestures;
138+
private Gesture mCurrentGesture;
139+
private int mLastSaveLen = -1;
140+
private String mLogfile;
141+
142+
private Handler mHandler = new Handler() {
143+
@Override
144+
public void handleMessage(Message msg) {
145+
if (msg.what == SAVE_MESSAGE) {
146+
save();
147+
}
148+
}
149+
};
150+
151+
public GestureRecorder(String filename) {
152+
mLogfile = filename;
153+
mGestures = new LinkedList<Gesture>();
154+
mCurrentGesture = null;
155+
}
156+
157+
public void add(MotionEvent ev) {
158+
synchronized (mGestures) {
159+
if (mCurrentGesture == null || mCurrentGesture.isComplete()) {
160+
mCurrentGesture = new Gesture();
161+
mGestures.add(mCurrentGesture);
162+
}
163+
mCurrentGesture.add(ev);
164+
}
165+
saveLater();
166+
}
167+
168+
public void tag(long when, String tag, String info) {
169+
synchronized (mGestures) {
170+
if (mCurrentGesture == null) {
171+
mCurrentGesture = new Gesture();
172+
mGestures.add(mCurrentGesture);
173+
}
174+
mCurrentGesture.tag(when, tag, info);
175+
}
176+
saveLater();
177+
}
178+
179+
public void tag(long when, String tag) {
180+
tag(when, tag, null);
181+
}
182+
183+
public void tag(String tag) {
184+
tag(SystemClock.uptimeMillis(), tag, null);
185+
}
186+
187+
public void tag(String tag, String info) {
188+
tag(SystemClock.uptimeMillis(), tag, info);
189+
}
190+
191+
/**
192+
* Generates a JSON string capturing all completed gestures.
193+
* Not threadsafe; call with a lock.
194+
*/
195+
public String toJsonLocked() {
196+
StringBuilder sb = new StringBuilder();
197+
boolean first = true;
198+
sb.append("[");
199+
int count = 0;
200+
for (Gesture g : mGestures) {
201+
if (!g.isComplete()) continue;
202+
if (!first) sb.append("," );
203+
first = false;
204+
sb.append(g.toJson());
205+
count++;
206+
}
207+
mLastSaveLen = count;
208+
sb.append("]");
209+
return sb.toString();
210+
}
211+
212+
public String toJson() {
213+
String s;
214+
synchronized (mGestures) {
215+
s = toJsonLocked();
216+
}
217+
return s;
218+
}
219+
220+
public void saveLater() {
221+
mHandler.removeMessages(SAVE_MESSAGE);
222+
mHandler.sendEmptyMessageDelayed(SAVE_MESSAGE, SAVE_DELAY);
223+
}
224+
225+
public void save() {
226+
synchronized (mGestures) {
227+
try {
228+
BufferedWriter w = new BufferedWriter(new FileWriter(mLogfile, /*append=*/ true));
229+
w.append(toJsonLocked() + "\n");
230+
w.close();
231+
mGestures.clear();
232+
// If we have a pending gesture, push it back
233+
if (!mCurrentGesture.isComplete()) {
234+
mGestures.add(mCurrentGesture);
235+
}
236+
if (DEBUG) {
237+
Slog.v(TAG, String.format("Wrote %d complete gestures to %s", mLastSaveLen, mLogfile));
238+
}
239+
} catch (IOException e) {
240+
Slog.e(TAG, String.format("Couldn't write gestures to %s", mLogfile), e);
241+
mLastSaveLen = -1;
242+
}
243+
}
244+
}
245+
246+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
247+
save();
248+
if (mLastSaveLen >= 0) {
249+
pw.println(String.valueOf(mLastSaveLen) + " gestures written to " + mLogfile);
250+
} else {
251+
pw.println("error writing gestures");
252+
}
253+
}
254+
}

packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
import com.android.systemui.recent.RecentTasksLoader;
8080
import com.android.systemui.statusbar.BaseStatusBar;
8181
import com.android.systemui.statusbar.CommandQueue;
82+
import com.android.systemui.statusbar.GestureRecorder;
8283
import com.android.systemui.statusbar.NotificationData;
8384
import com.android.systemui.statusbar.NotificationData.Entry;
8485
import com.android.systemui.statusbar.RotationToggle;
@@ -242,6 +243,9 @@ public class PhoneStatusBar extends BaseStatusBar {
242243

243244
DisplayMetrics mDisplayMetrics = new DisplayMetrics();
244245

246+
// XXX: gesture research
247+
private GestureRecorder mGestureRec = new GestureRecorder("/sdcard/statusbar_gestures.dat");
248+
245249
private int mNavigationIconHints = 0;
246250
private final Animator.AnimatorListener mMakeIconsInvisible = new AnimatorListenerAdapter() {
247251
@Override
@@ -1570,6 +1574,8 @@ boolean interceptTouchEvent(MotionEvent event) {
15701574
}
15711575
}
15721576

1577+
mGestureRec.add(event);
1578+
15731579
if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
15741580
return false;
15751581
}
@@ -1604,6 +1610,7 @@ boolean interceptTouchEvent(MotionEvent event) {
16041610
if (x >= edgeBorder && x < mDisplayMetrics.widthPixels - edgeBorder) {
16051611
prepareTracking(y, !mExpanded);// opening if we're not already fully visible
16061612
trackMovement(event);
1613+
mGestureRec.tag("tracking", mExpanded ? "expanded" : "collapsed");
16071614
}
16081615
}
16091616
} else if (mTracking) {
@@ -1654,6 +1661,8 @@ boolean interceptTouchEvent(MotionEvent event) {
16541661
mFlingY = y;
16551662
}
16561663
mFlingVelocity = vel;
1664+
mGestureRec.tag("fling " + ((mFlingVelocity > 0) ? "open" : "closed"),
1665+
"v=" + mFlingVelocity);
16571666
mHandler.post(mPerformFling);
16581667
}
16591668

@@ -1938,6 +1947,9 @@ public void run() {
19381947
}
19391948
}
19401949

1950+
pw.print(" status bar gestures: ");
1951+
mGestureRec.dump(fd, pw, args);
1952+
19411953
mNetworkController.dump(fd, pw, args);
19421954
}
19431955

0 commit comments

Comments
 (0)