I need to make a complete log of all the actions of a user in an activity:
For example:
click on R.id.btn_ok view
longclick on R.id.listview-item
I need to make a complete log of all the actions of a user in an activity:
For example:
click on R.id.btn_ok view
longclick on R.id.listview-item
I found the challenge interesting so I made two small classes, TouchEventLogger to log the "Touch events" and GestureEventLogger in> "Gesture events" .
Both inherit from MotionEventLogger .
MotionEventLogger.java
public abstract class MotionEventLogger {
public interface Logger{
public void log(String text);
}
protected Logger logger;
private String TAG = MotionEventLogger.class.getSimpleName();;
public MotionEventLogger(Logger logger) {
if(logger == null){
this.logger = new DefaultLogger(TAG);
}else{
this.logger = logger;
}
}
abstract void log(MotionEvent event);
protected static class DefaultLogger implements Logger{
final String TAG;
public DefaultLogger(String TAG) {
this.TAG = TAG;
}
@Override
public void log(String text) {
Log.i(TAG, text);
}
}
//Fonte: http://stackoverflow.com/a/14985355/2556111
protected View findViewAtPosition(View parent, int x, int y) {
if (parent instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup)parent;
for (int i=0; i<viewGroup.getChildCount(); i++) {
View child = viewGroup.getChildAt(i);
View viewAtPosition = findViewAtPosition(child, x, y);
if (viewAtPosition != null) {
return viewAtPosition;
}
}
return parent;// null;
} else {
Rect rect = new Rect();
parent.getGlobalVisibleRect(rect);
if (rect.contains(x, y)) {
return parent;
} else {
return null;
}
}
}
protected String getViewIdText(View viewTouched) {
String viewIdText;
if(viewTouched.getId() == View.NO_ID){
viewIdText = viewTouched.getClass().getSimpleName() + " sem id";
}else{
viewIdText = viewTouched.getResources().getResourceEntryName(viewTouched.getId());
}
return viewIdText;
}
}
TouchEventLogger.java
public class TouchEventLogger extends MotionEventLogger {
private ViewGroup contentView;
private int touchSlop;
private boolean canLogMove = false;
public TouchEventLogger(ViewGroup viewGroup, Logger logger) {
super(logger);
contentView = viewGroup;
touchSlop = getTouchSlop(viewGroup);
}
public TouchEventLogger(ViewGroup viewGroup) {
this(viewGroup, null);
}
public TouchEventLogger(Activity activity, Logger loger) {
super(loger);
contentView = (ViewGroup)((ViewGroup)activity.findViewById(android.R.id.content)).getChildAt(0);
touchSlop = getTouchSlop(contentView);
}
public TouchEventLogger(Activity activity) {
this(activity, null);
}
@Override
public void log(MotionEvent event){
float initialX = 0, initialY = 0;
int action = event.getActionMasked();
int eventX = (int) event.getX();
int eventY = (int) event.getY();
View viewTouched= findViewAtPosition(contentView, eventX, eventY);
if(viewTouched == null)return;
String viewIdText = getViewIdText(viewTouched);
switch (action) {
case MotionEvent.ACTION_DOWN:
initialX = eventX;
initialY = eventY;
canLogMove = true;
logger.log(viewIdText + " Action DOWN");
break;
case MotionEvent.ACTION_MOVE:
float xDiff = initialX - eventX;
float yDiff = initialY - eventY;
if(canLogMove && (Math.abs(xDiff) > touchSlop || Math.abs(yDiff) > touchSlop)) {
logger.log(viewIdText + " Action Move");
canLogMove = false;
}
break;
case MotionEvent.ACTION_UP:
logger.log(viewIdText + " Action UP");
break;
case MotionEvent.ACTION_CANCEL:
logger.log(viewIdText + " Action CANCEL");
break;
case MotionEvent.ACTION_OUTSIDE:
logger.log(viewIdText + " Movement occurred outside bounds of current screen element");
break;
}
}
private int getTouchSlop(View view){
ViewConfiguration vc = ViewConfiguration.get(view.getContext());
return vc.getScaledTouchSlop();
}
}
GestureEventLogger.java
public class GestureEventLogger extends MotionEventLogger implements
GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener{
private GestureDetector gestureDetector;
private String viewIdText;
protected ViewGroup contentView;
public GestureEventLogger(Activity activity, Logger loger) {
super(loger);
contentView = (ViewGroup)((ViewGroup)activity.findViewById(android.R.id.content)).getChildAt(0);
setDetector(activity);
}
public GestureEventLogger(Activity activity) {
this(activity, (Logger)null);
}
public GestureEventLogger(Context context, ViewGroup viewGroup, Logger logger) {
super(logger);
contentView = viewGroup;
setDetector(context);
}
public GestureEventLogger(Context context, ViewGroup viewGroup) {
this(context, viewGroup, null);
setDetector(context);
}
@Override
public void log(MotionEvent event) {
View viewTouched= findViewAtPosition(contentView, (int)event.getX(), (int)event.getY());
if(viewTouched != null){
viewIdText = getViewIdText(viewTouched);
gestureDetector.onTouchEvent(event);
}
}
@Override
public boolean onDoubleTap(MotionEvent arg0) {
logger.log(viewIdText + " DoubleTap");
return true;
}
@Override
public boolean onDoubleTapEvent(MotionEvent arg0) {
//logger.log(viewIdText + " DoubleTapEvent");
return false;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent arg0) {
logger.log(viewIdText + " SingleTap");
return true;
}
@Override
public boolean onDown(MotionEvent arg0) {
//logger.log(viewIdText + " Down");
return false;
}
@Override
public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2,
float arg3) {
logger.log(viewIdText + " Fling");
return true;
}
@Override
public void onLongPress(MotionEvent arg0) {
logger.log(viewIdText + " LongPress");
}
@Override
public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2,
float arg3) {
//logger.log(viewIdText + " Scroll");
return false;
}
@Override
public void onShowPress(MotionEvent arg0) {
//logger.log(viewIdText + " ShowPress");
}
@Override
public boolean onSingleTapUp(MotionEvent arg0) {
//logger.log(viewIdText + " SingleTapUp");
return false;
}
private void setDetector(Context context) {
gestureDetector = new GestureDetector(context, this);
gestureDetector.setOnDoubleTapListener(this);
}
}
Use them as follows:
Declare a private variable to Activity :
private MotionEventLogger eventLogger;
In the onCreate()
method, create an TouchEventLogger or GestureEventLogger instance:
eventLogger = new TouchEventLogger(this);
//ou
//eventLogger = new GestureEventLogger(this);
Override method dispatchTouchEvent()
and call eventLogger.log(event);
:
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
eventLogger.log(event);
return super.dispatchTouchEvent(event);
}
By default log is done in LogCat.
You can do your own logger , make it implement the interface MotionEventLogger.Logger
and pass it on the constructor.
onCreate(){
setContentView(R.layout.activity_main);
View view = findViewById(R.id.main);
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view,MotionEvent event) {
return true; }
});