com.eg.agent.android.gestures.GestureReporter Maven / Gradle / Ivy
The newest version!
package com.eg.agent.android.gestures;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicReference;
import org.json.JSONObject;
import com.eg.agent.android.BaseAndroidAgent;
import com.eg.agent.android.EGAgent;
import com.eg.agent.android.MobileAgentUpload;
import com.eg.agent.android.Queue;
import com.eg.agent.android.analytics.Attributes;
import com.eg.agent.android.connectivity.UserActionFacade;
import com.eg.agent.android.connectivity.UserActionType;
import com.eg.agent.android.instrumentation.InstrumentView;
import com.eg.agent.android.instrumentation.ViewListeners;
import com.eg.agent.android.logging.EGAgentLog;
import com.eg.agent.android.logging.EGAgentLogManager;
import com.eg.agent.android.util.ParameterConstants;
import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.Activity;
import android.content.res.Resources;
import android.graphics.Rect;
import android.text.TextUtils;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
@TargetApi(value = 28)
public class GestureReporter extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener {
final Activity activity;
final View rootView;
final HashMap controlCache = new HashMap();
final ExecutorService executor = Executors.newSingleThreadExecutor();
private String gestureId;
private static Set supportedClasses = new HashSet(Arrays.asList(TextView.class, EditText.class,
Button.class, ProgressBar.class, ImageButton.class, CheckBox.class, DatePicker.class, ImageView.class,
ActionBar.class, AdapterView.class, ViewStub.class));
private static final Map gestureReporterMap = new HashMap();
private static final AtomicReference currentGestureReporter = new AtomicReference(
null);
private static final EGAgentLog log = EGAgentLogManager.getAgentLog();
private long startTime;
private long endTime;
public GestureReporter(Activity activity, View rootView) {
this.activity = activity;
this.rootView = rootView;
this.gestureId = this.generateGestureId(rootView);
this.addView(rootView);
log.info("[Gesture] Attached to activity [" + activity.getLocalClassName() + "]");
}
public void shutdown() {
try {
this.executor.shutdown();
this.controlCache.clear();
} catch (Exception exception) {
log.error(exception.getMessage());
}
}
public Set addView(View view) {
Set childViews = GestureReporter.gatherChildViews(view);
childViews.addAll(view.getTouchables());
this.attachChildTouchListeners(childViews);
InstrumentView.setOnTouchListener(view, this);
log.verbose("[Gesture] Attached to view [" + view.toString() + "]");
return childViews;
}
public boolean onDown(final MotionEvent e) {
this.bg(new Runnable() {
@Override
public void run() {
GestureReporter.this.startTime = System.currentTimeMillis();
Map attributes = GestureReporter.this.createGestureMotionEventAttributes(null, e);
log.verbose("[Gesture] onDown MotionEvent[" + e.toString() + "]");
attributes.put("event", "onDown");
GestureReporter.this.endTime = System.currentTimeMillis();
populateAttributes(GestureReporter.this, attributes);
UserActionFacade.getInstance().recordUserAction(UserActionType.Tap, attributes);
}
});
return true;
}
public boolean onTouch(final View v, final MotionEvent event) {
this.bg(new Runnable() {
@Override
public void run() {
GestureReporter.this.startTime = System.currentTimeMillis();
Map attributes = GestureReporter.this.createGestureMotionEventAttributes(null, event);
log.verbose("[Gesture] onTouch [" + v.getClass().getSimpleName() + "] MotionEvent[" + event.toString()
+ "]");
attributes.put("event", "onTouch");
GestureReporter.this.endTime = System.currentTimeMillis();
populateAttributes(GestureReporter.this, attributes);
UserActionFacade.getInstance().recordUserAction(UserActionType.Tap, attributes);
}
});
return false;
}
public boolean onFling(final MotionEvent event1, final MotionEvent event2, final float velocityX,
final float velocityY) {
this.bg(new Runnable() {
@Override
public void run() {
GestureReporter.this.startTime = System.currentTimeMillis();
Map attributes = GestureReporter.this.createGestureMotionEventAttributes(null, event1);
log.verbose("[Gesture] onFling: MotionEvent1[" + event1.toString() + "] MotionEvent2["
+ event2.toString() + "] velX[" + velocityX + "] velY[" + velocityY + "]" + attributes);
attributes.put("event", "onFling");
GestureReporter.this.endTime = System.currentTimeMillis();
populateAttributes(GestureReporter.this, attributes);
UserActionFacade.getInstance().recordUserAction(UserActionType.Swipe, attributes);
}
});
return false;
}
public boolean onSingleTapConfirmed(final MotionEvent event) {
this.bg(new Runnable() {
@Override
public void run() {
GestureReporter.this.startTime = System.currentTimeMillis();
Map attributes = GestureReporter.this.createGestureMotionEventAttributes(null, event);
log.verbose("[Gesture] onSingleTapConfirmed: MotionEvent[" + event.toString() + "]" + attributes);
attributes.put("event", "onSingleTapConfirmed");
GestureReporter.this.endTime = System.currentTimeMillis();
populateAttributes(GestureReporter.this, attributes);
UserActionFacade.getInstance().recordUserAction(UserActionType.Tap, attributes);
}
});
return false;
}
public boolean onDoubleTap(final MotionEvent event) {
this.bg(new Runnable() {
@Override
public void run() {
GestureReporter.this.startTime = System.currentTimeMillis();
Map attributes = GestureReporter.this.createGestureMotionEventAttributes(null, event);
log.verbose("[Gesture] onDoubleTap: MotionEvent[" + event.toString() + "]" + attributes);
attributes.put("event", "onDoubleTap");
GestureReporter.this.endTime = System.currentTimeMillis();
populateAttributes(GestureReporter.this, attributes);
UserActionFacade.getInstance().recordUserAction(UserActionType.DoubleTap, attributes);
}
});
return false;
}
protected Map createGestureMotionEventAttributes(View v, MotionEvent event) {
HashMap attributes = new HashMap();
attributes.put("touchCoordinates", String.format("[%d,%d]", (int) event.getRawX(), (int) event.getRawY()));
attributes.put("action", MotionEvent.actionToString(event.getAction()));
attributes.put("eventTime", ParameterConstants.FORMATTER.format(event.getEventTime() / 1000) + " (in Secs)");
attributes.put("downTime", ParameterConstants.FORMATTER.format(event.getDownTime() / 1000) + " (in Secs)");
final int pointerCount = event.getPointerCount();
for (int i = 0; i < pointerCount; i++) {
attributes.put("toolType[" + i + "]", event.getToolType(i));
}
if (v == null) {
v = this.findViewByMotionEvent(event);
}
log.debug("GestureReporter:createGestureMotionEventAttributes:view::" + v);
if (v != null) {
attributes.putAll(this.createGestureViewAttributes(v));
}
log.debug("GestureReporter:createGestureMotionEventAttributes:attributes::" + attributes);
return attributes;
}
protected Map createGestureViewAttributes(View view) {
log.debug("GestureReporter:createGestureViewAttributes:view::" + view);
HashMap attributes = new HashMap();
if (EGAgent.getImpl().getResourceInforamtion() == null) {
EGAgent.setImpl(BaseAndroidAgent.getInstance());
}
attributes.put("orientation", getOrientation());
log.debug("GestureReporter:createGestureViewAttributes:orientation::" + attributes.get("orientation"));
if (view != null) {
ViewListeners.ListenerElement element;
Rect rect = new Rect();
this.gestureId = this.generateGestureId(view);
view.getGlobalVisibleRect(rect);
attributes.put("viewId", view.getId());
attributes.put("viewName", view.getClass().getSimpleName());
try {
attributes.put("viewTypeName", view.getResources().getResourceTypeName(view.getId()));
} catch (Exception e) {
log.error(e.getMessage());
attributes.put("viewTypeName", view.getClass().getSimpleName());
}
try {
attributes.put("viewEntryName", view.getResources().getResourceEntryName(view.getId()));
} catch (Exception e) {
log.error(e.getMessage());
attributes.put("viewEntryName", view.getRootView().getClass().getSimpleName());
}
try {
attributes.put("label", view.getResources().getResourceEntryName(view.getId()));
} catch (Exception e) {
attributes.put("label", view.getTag());
log.error(e.getMessage());
}
attributes.put("viewRectangle", String.format(Locale.getDefault(), "[[%d,%d],[%d,%d]]", rect.left, rect.top,
rect.left + rect.width(), rect.top + rect.height()));
if (view.getContentDescription() != null) {
attributes.put("contentDescription", view.getContentDescription());
}
if ((element = ViewListeners.getListenerElement(view.hashCode())) != null) {
attributes.putAll(this.createGestureControlAttributes(element));
}
if (GestureObserver.isSubClassOf((Object) view, TextView.class)) {
try {
TextView textView = (TextView) view;
String label = textView.getText().toString();
if (!TextUtils.isEmpty((CharSequence) label)) {
attributes.put("label", label);
}
} catch (Exception textView) {
log.error(textView.getMessage());
}
}
}
log.debug("GestureReporter::createGestureViewAttributes:::attributes:::" + attributes);
return attributes;
}
protected Map createGestureControlAttributes(ViewListeners.ListenerElement element) {
HashMap attributes = new HashMap();
if (element != null) {
attributes.put("targetObject", element.targetObject);
attributes.put("methodExecuted", element.methodExecuted);
}
return attributes;
}
protected boolean shouldTrackControl(View view) {
try {
for (Class clazz : supportedClasses) {
if (!GestureObserver.isSubClassOf((Object) view, clazz))
continue;
return true;
}
} catch (Exception exception) {
log.error(exception.getMessage());
}
return false;
}
private void attachChildTouchListeners(Set childViews) {
for (View childView : childViews) {
this.controlCache.put(childView.hashCode(), childView);
InstrumentView.setOnTouchListener(childView, this);
log.verbose("[Gesture] Will monitor touch events on view [" + childView.toString() + "]");
}
}
protected static Set gatherChildViews(View v) {
HashSet childViews = new HashSet();
if (!GestureObserver.isSubClassOf((Object) v, ViewGroup.class)) {
childViews.add(v);
return childViews;
}
ViewGroup vg = (ViewGroup) v;
int childCnt = Math.min(64, vg.getChildCount());
if (childCnt == 0) {
childViews.add(v);
} else {
for (int i = 0; i < childCnt; ++i) {
View child = vg.getChildAt(i);
childViews.addAll(GestureReporter.gatherChildViews(child));
}
}
return childViews;
}
private boolean resourceHasPackage(int resid) {
return resid >>> 24 != 0;
}
protected String generateGestureId(View view) {
String viewId = Integer.valueOf(view.getId()).toString();
int id = view.getId();
if (id != -1) {
StringBuilder out = new StringBuilder(1024);
out.append(view.getClass().getName());
out.append('{');
Resources r = view.getResources();
if (this.resourceHasPackage(id) && r != null) {
try {
String pkgname;
switch (id & -16777216) {
case 2130706432: {
pkgname = "app";
break;
}
case 16777216: {
pkgname = "android";
break;
}
default: {
pkgname = r.getResourcePackageName(id);
}
}
String typename = r.getResourceTypeName(id);
String entryname = r.getResourceEntryName(id);
out.append(pkgname);
out.append(":");
out.append(typename);
out.append("/");
out.append(entryname);
} catch (Exception pkgname) {
log.error(pkgname.getMessage());
}
}
out.append("}");
viewId = out.toString();
} else {
viewId = UUID.randomUUID().toString();
}
return viewId;
}
protected View findViewByMotionEvent(MotionEvent event) {
View targetView = null;
Rect rect = new Rect();
int rawX = (int) event.getRawX();
int rawY = (int) event.getRawY();
for (View view : this.controlCache.values()) {
view.getGlobalVisibleRect(rect);
if (!rect.contains(rawX, rawY))
continue;
targetView = view;
break;
}
return targetView;
}
void bg(Runnable runnable) {
try {
this.executor.submit(runnable);
} catch (RejectedExecutionException e) {
log.error("GestureReporter: " + e);
}
}
static GestureReporter provideGestureReporter(Activity activity, View rootView) {
if (!gestureReporterMap.containsKey(activity.hashCode())) {
currentGestureReporter.set(new GestureReporter(activity, rootView));
gestureReporterMap.put(activity.hashCode(), currentGestureReporter.get());
}
return gestureReporterMap.get(activity.hashCode());
}
public static GestureReporter getGestureReporter() {
return currentGestureReporter.get();
}
static GestureReporter getGestureReporter(Activity activity) {
return gestureReporterMap.get(activity.hashCode());
}
static GestureReporter invalidate(Activity activity) {
GestureReporter reporter = gestureReporterMap.remove(activity.hashCode());
if (reporter != null) {
reporter.shutdown();
}
currentGestureReporter.compareAndSet(reporter, null);
return reporter;
}
public static void recordOnTouch(final View v, final MotionEvent event, final Map attributes) {
final GestureReporter gestureReporter = GestureReporter.getGestureReporter();
if (gestureReporter != null) {
gestureReporter.bg(new Runnable() {
@Override
public void run() {
gestureReporter.startTime = System.currentTimeMillis();
try {
Map attrs = gestureReporter.createGestureMotionEventAttributes(v, event);
if (attributes != null) {
attrs.putAll(attributes);
}
attrs.put("event", "onTouch");
log.verbose("[Gesture] recordOnTouch [" + v.getClass().getSimpleName() + "] MotionEvent["
+ event.toString() + "] Attributes [" + attributes + "]");
gestureReporter.endTime = System.currentTimeMillis();
populateAttributes(gestureReporter, attrs);
UserActionFacade.getInstance().recordUserAction(UserActionType.Tap, attributes);
} catch (Throwable t) {
log.error(t.getMessage(), t);
}
}
});
}
}
public static void recordOnClick(final View v, final Map attributes) {
final GestureReporter gestureReporter = GestureReporter.getGestureReporter();
log.verbose("recordOnlik gestureReporter " + gestureReporter);
if (gestureReporter != null) {
gestureReporter.bg(new Runnable() {
@Override
public void run() {
try {
gestureReporter.startTime = System.currentTimeMillis();
Map attrs = gestureReporter.createGestureViewAttributes(v);
if (attributes != null) {
attrs.putAll(attributes);
}
log.verbose("[Gesture] onClick [" + v.getClass().getSimpleName() + "] " + attrs);
attrs.put("event", "onClick");
gestureReporter.endTime = System.currentTimeMillis();
populateAttributes(gestureReporter, attrs);
UserActionFacade.getInstance().recordUserAction(UserActionType.Tap, attrs);
} catch (Throwable t) {
log.error(t.getMessage(), t);
}
}
});
}
}
public static void recordOnLongClick(final View v, final Map attributes) {
final GestureReporter gestureReporter = GestureReporter.getGestureReporter();
if (gestureReporter != null) {
gestureReporter.bg(new Runnable() {
@Override
public void run() {
try {
gestureReporter.startTime = System.currentTimeMillis();
Map attrs = gestureReporter.createGestureViewAttributes(v);
if (attributes != null) {
attrs.putAll(attributes);
}
log.verbose("[Gesture] onLongClick [" + v.getClass().getSimpleName() + "]" + attrs);
attrs.put("event", "onLongClick");
gestureReporter.endTime = System.currentTimeMillis();
populateAttributes(gestureReporter, attrs);
UserActionFacade.getInstance().recordUserAction(UserActionType.DoubleTap, attrs);
} catch (Throwable t) {
log.error(t.getMessage(), t);
}
}
});
}
}
private static void populateAttributes(GestureReporter reporter, Map attributes) {
try {
JSONObject gesture = new JSONObject();
for (Object obj : attributes.entrySet()) {
Map.Entry entry = (Map.Entry) obj;
gesture.put(entry.getKey().toString(), entry.getValue());
}
if (attributes.get("viewEntryName") == null) {
gesture.put("viewEntryName", attributes.get("viewName"));
}
if (attributes.get("viewTypeName") == null) {
gesture.put("viewTypeName", attributes.get("viewName"));
}
if (attributes.get("label") == null) {
gesture.put("label", attributes.get("viewName"));
}
gesture.put("orientation", getOrientation());
attributes.put(ParameterConstants.gestureData, gesture);
BaseAndroidAgent androidAgentimpl = BaseAndroidAgent.getInstance();
attributes.put(ParameterConstants.instrumentationType, "Gesture");
attributes.put(ParameterConstants.activityName, reporter.activity.getClass().getSimpleName());
// attributes.get("viewEntryName") == null ? attributes.get("viewName")
// : attributes.get("viewEntryName") + "#" + attributes.get("viewName"));
attributes.put(ParameterConstants.executionTime, (reporter.endTime - reporter.startTime));
attributes.put(ParameterConstants.activityStartTime, reporter.startTime);
attributes.put(ParameterConstants.activityEndTime, reporter.endTime);
//attributes.put(ParameterConstants.launcherActivity, reporter.activity.getClass().getSimpleName());
attributes.put(ParameterConstants.launcherActivity, androidAgentimpl.getApplicationInformation().getLauncherActivity());
attributes.put(ParameterConstants.executionMethodName,
((attributes.get("label") == null || attributes.get("label").toString().trim().length() == 0)
? reporter.rootView.getClass().getSimpleName() + "#" + attributes.get("event")
: (attributes.get("label")) + "#" + attributes.get("event")));
attributes.put(ParameterConstants.appName, androidAgentimpl.getApplicationInformation().getAppName());
attributes.put(ParameterConstants.deviceName, androidAgentimpl.getDeviceInformation().getDeviceName());
attributes.put(ParameterConstants.osName, androidAgentimpl.getDeviceInformation().getOsName());
attributes.put(ParameterConstants.osVersion, androidAgentimpl.getDeviceInformation().getOsVersion());
attributes.put(ParameterConstants.appToken, androidAgentimpl.getAgentConfiguration().getApplicationToken());
attributes.put("orientation", getOrientation());
attributes.put(ParameterConstants.deviceInformation, MobileAgentUpload.getDevInfo());
attributes.put(ParameterConstants.applicationInformation, MobileAgentUpload.getAppinfo());
attributes.put(ParameterConstants.resourceInformatin, MobileAgentUpload.getResInfo());
attributes.put(ParameterConstants.country, androidAgentimpl.getApplicationInformation().getCountry());
// attributes.put(ParameterConstants.customInformation, MobileAgentUpload.getCustomInfo());
attributes.put(ParameterConstants.userInformation, MobileAgentUpload.getUserInfo());
} catch (Exception e) {
e.printStackTrace();
}
Queue.queue(getJSONObject(attributes));
log.debug("GestureReporter:populateAttributes:::attributes:::" + attributes);
}
public static JSONObject getJSONObject(Collection collection) {
JSONObject mainJson = new JSONObject();
try {
for (Attributes obj : collection) {
mainJson.put(obj.getName(), obj.valueAsString());
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
log.debug("GestureReporter:getJSONObject:::mainJson1::" + mainJson);
return mainJson;
}
public static String getOrientation() {
String orientation = "Portrait";
if (BaseAndroidAgent.getInstance().getResourceInforamtion() != null) {
orientation = BaseAndroidAgent.getInstance().getResourceInforamtion().getOrientation();
}
return orientation;
}
public static JSONObject getJSONObject(Map attributes) {
JSONObject mainJson = new JSONObject();
try {
for (Object obj : attributes.entrySet()) {
Map.Entry entry = (Map.Entry) obj;
mainJson.put(entry.getKey().toString(), entry.getValue());
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
log.debug("GestureReporter:getJSONObject:::mainJson2::" + mainJson);
return mainJson;
}
private static void postData(GestureReporter reporter, Map attributes) {
try {
log.debug("GestureReporter:postData:::attributes:::" + attributes);
// populateAttributes(reporter, attributes);
Queue.queue(getJSONObject(attributes));
} catch (Exception e) {
e.printStackTrace();
}
}
public void onClick(View v) {
}
public boolean onLongClick(View v) {
return false;
}
public void onItemClick(AdapterView> arg0, View arg1, int arg2, long arg3) {
}
public void onItemSelected(final AdapterView> arg0, View arg1, int arg2, long arg3) {
}
public void onNothingSelected(final AdapterView> arg0) {
}
public boolean onItemLongClick(AdapterView> arg0, View arg1, int arg2, long arg3) {
return false;
}
}