All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy