package com.frogermcs.androiddevmetrics.internal.metrics;
import android.app.Activity;
import android.support.annotation.NonNull;
import android.support.v7.view.WindowCallbackWrapper;
import android.view.Window;
import com.frogermcs.androiddevmetrics.internal.ActivityMetricDescription;
import com.frogermcs.androiddevmetrics.internal.metrics.model.FpsDropMetric;
import com.frogermcs.androiddevmetrics.internal.ui.MetricsActivity;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Created by Miroslaw Stanek on 28.02.2016.
*/
public class ActivityLifecycleMetrics {
private static class Holder {
static final ActivityLifecycleMetrics INSTANCE = new ActivityLifecycleMetrics();
}
public static ActivityLifecycleMetrics getInstance() {
return Holder.INSTANCE;
}
public LinkedHashMap<Integer, ActivityLifecycleMetric> activityLifecycleMetricsMap = new LinkedHashMap<>();
public void logPreOnCreate(Activity activity) {
if (isIgnoredActivity(activity)) return;
int activityHash = activity.hashCode();
ActivityLifecycleMetric activityLifecycleMetric = activityLifecycleMetricsMap.get(activityHash);
if (activityLifecycleMetric == null) {
activityLifecycleMetric = setupNewActivityLifecycleMetric(activity, activityHash);
}
activityLifecycleMetric.state = ActivityLifecycleMetric.STATE_PRE_CREATED;
activityLifecycleMetric.hasOnCreateImplemented = true;
}
public void logPostOnCreate(Activity activity) {
if (isIgnoredActivity(activity)) return;
int activityHash = activity.hashCode();
ActivityLifecycleMetric activityLifecycleMetric = activityLifecycleMetricsMap.get(activityHash);
if (activityLifecycleMetric == null) {
activityLifecycleMetric = setupNewActivityLifecycleMetric(activity, activityHash);
}
activityLifecycleMetric.postCreateNanoTime = System.nanoTime();
activityLifecycleMetric.state = ActivityLifecycleMetric.STATE_POST_CREATED;
Window window = activity.getWindow();
window.setCallback(new MetricWindowCallbackWrapper(window.getCallback(), activityLifecycleMetric));
}
@NonNull
private ActivityLifecycleMetric setupNewActivityLifecycleMetric(Activity activity, int activityHash) {
ActivityLifecycleMetric activityLifecycleMetric;
activityLifecycleMetric = new ActivityLifecycleMetric();
activityLifecycleMetric.activityClass = activity.getClass();
activityLifecycleMetric.preCreateNanoTime = System.nanoTime();
activityLifecycleMetric.isFirstActivity = activityLifecycleMetricsMap.size() == 0;
activityLifecycleMetricsMap.put(activityHash, activityLifecycleMetric);
return activityLifecycleMetric;
}
public void logPreOnStart(Activity activity) {
if (isIgnoredActivity(activity)) return;
int activityHash = activity.hashCode();
ActivityLifecycleMetric activityLifecycleMetric = activityLifecycleMetricsMap.get(activityHash);
if (activityLifecycleMetric.state == ActivityLifecycleMetric.STATE_POST_CREATED) {
activityLifecycleMetric.preStartNanoTime = System.nanoTime();
activityLifecycleMetric.state = ActivityLifecycleMetric.STATE_PRE_STARTED;
activityLifecycleMetric.hasOnStartImplemented = true;
}
}
public void logPostOnStart(Activity activity) {
if (isIgnoredActivity(activity)) return;
int activityHash = activity.hashCode();
ActivityLifecycleMetric activityLifecycleMetric = activityLifecycleMetricsMap.get(activityHash);
if (activityLifecycleMetric.state == ActivityLifecycleMetric.STATE_POST_CREATED
|| activityLifecycleMetric.state == ActivityLifecycleMetric.STATE_PRE_STARTED
|| activityLifecycleMetric.state == ActivityLifecycleMetric.STATE_POST_STARTED) {
if (activityLifecycleMetric.state == ActivityLifecycleMetric.STATE_POST_CREATED) {
activityLifecycleMetric.preStartNanoTime = System.nanoTime();
}
activityLifecycleMetric.postStartNanoTime = System.nanoTime();
activityLifecycleMetric.state = ActivityLifecycleMetric.STATE_POST_STARTED;
}
}
public void logPreOnResume(Activity activity) {
if (isIgnoredActivity(activity)) return;
int activityHash = activity.hashCode();
ActivityLifecycleMetric activityLifecycleMetric = activityLifecycleMetricsMap.get(activityHash);
if (activityLifecycleMetric.state == ActivityLifecycleMetric.STATE_POST_STARTED) {
activityLifecycleMetric.preResumeNanoTime = System.nanoTime();
activityLifecycleMetric.state = ActivityLifecycleMetric.STATE_PRE_RESUMED;
activityLifecycleMetric.hasOnResumeImplemented = true;
}
}
public void logPostOnResume(Activity activity) {
if (isIgnoredActivity(activity)) return;
int activityHash = activity.hashCode();
ActivityLifecycleMetric activityLifecycleMetric = activityLifecycleMetricsMap.get(activityHash);
if (activityLifecycleMetric.state == ActivityLifecycleMetric.STATE_POST_STARTED
|| activityLifecycleMetric.state == ActivityLifecycleMetric.STATE_PRE_RESUMED
|| activityLifecycleMetric.state == ActivityLifecycleMetric.STATE_POST_RESUMED) {
if (activityLifecycleMetric.state == ActivityLifecycleMetric.STATE_POST_STARTED) {
activityLifecycleMetric.preResumeNanoTime = System.nanoTime();
}
activityLifecycleMetric.postResumeNanoTime = System.nanoTime();
activityLifecycleMetric.state = ActivityLifecycleMetric.STATE_POST_RESUMED;
}
}
public void logOnPaused(Activity activity) {
if (isIgnoredActivity(activity)) return;
updateActivityMetricState(activity, ActivityLifecycleMetric.STATE_PAUSED);
}
public void logOnStopped(Activity activity) {
if (isIgnoredActivity(activity)) return;
updateActivityMetricState(activity, ActivityLifecycleMetric.STATE_STOPPED);
}
public void logOnDestroyed(Activity activity) {
if (isIgnoredActivity(activity)) return;
updateActivityMetricState(activity, ActivityLifecycleMetric.STATE_DESTROYED);
}
private void updateActivityMetricState(Activity activity, int state) {
int activityHash = activity.hashCode();
ActivityLifecycleMetric activityLifecycleMetric = activityLifecycleMetricsMap.get(activityHash);
if (activityLifecycleMetric != null) {
activityLifecycleMetric.state = state;
}
}
public boolean isIgnoredActivity(Activity activity) {
return activity instanceof MetricsActivity;
}
public List<ActivityMetricDescription> getListOfMetricDescriptions() {
LinkedHashMap<String, ActivityMetricDescription> activityMetricDescriptions = new LinkedHashMap<>();
for (ActivityLifecycleMetric activityLifecycleMetric : activityLifecycleMetricsMap.values()) {
ActivityMetricDescription activityMetricDescription = activityMetricDescriptions.get(activityLifecycleMetric.activityClass.getSimpleName());
if (activityMetricDescription == null) {
activityMetricDescription = ActivityMetricDescription.initFrom(activityLifecycleMetric);
activityMetricDescriptions.put(activityLifecycleMetric.activityClass.getSimpleName(), activityMetricDescription);
} else {
activityMetricDescription.updateWith(activityLifecycleMetric);
}
}
ChoreographerMetrics.getInstance().collectDropsIfAny();
for (FpsDropMetric fpsDropMetric : ChoreographerMetrics.getInstance().dropMetricsList) {
ActivityMetricDescription activityMetricDescription = activityMetricDescriptions.get(fpsDropMetric.activityName);
if (activityMetricDescription != null) {
activityMetricDescription.updateWith(fpsDropMetric);
}
}
return new ArrayList<>(activityMetricDescriptions.values());
}
public static class ActivityLifecycleMetric {
public static final int STATE_NEW = 0;
public static final int STATE_PRE_CREATED = 1;
public static final int STATE_POST_CREATED = 2;
public static final int STATE_PRE_STARTED = 3;
public static final int STATE_POST_STARTED = 4;
public static final int STATE_PRE_RESUMED = 5;
public static final int STATE_POST_RESUMED = 6;
public static final int STATE_VISIBLE = 7;
public static final int STATE_PAUSED = 8;
public static final int STATE_STOPPED = 9;
public static final int STATE_DESTROYED = 10;
public Class activityClass;
public long preCreateNanoTime;
public long postCreateNanoTime;
public long preStartNanoTime;
public long postStartNanoTime;
public long preResumeNanoTime;
public long postResumeNanoTime;
public long viewVisibleToUserNanoTime;
public int state = STATE_NEW;
public boolean isFirstActivity = false;
public boolean hasOnCreateImplemented = false;
public boolean hasOnStartImplemented = false;
public boolean hasOnResumeImplemented = false;
public long createTimeMillis() {
if (state > STATE_POST_CREATED) {
return TimeUnit.NANOSECONDS.toMillis(postCreateNanoTime - preCreateNanoTime);
} else {
return -1;
}
}
public long startTimeMillis() {
if (state > STATE_POST_STARTED) {
return TimeUnit.NANOSECONDS.toMillis(postStartNanoTime - preStartNanoTime);
} else {
return -1;
}
}
public long resumeTimeMillis() {
if (state > STATE_POST_RESUMED) {
return TimeUnit.NANOSECONDS.toMillis(postResumeNanoTime - preResumeNanoTime);
} else {
return -1;
}
}
public long visibleTimeMillis() {
if (state >= STATE_VISIBLE) {
return TimeUnit.NANOSECONDS.toMillis(viewVisibleToUserNanoTime - postResumeNanoTime);
} else {
return -1;
}
}
@Override
public String toString() {
return "ActivityLifecycleMetric{" +
"activityClass=" + activityClass +
",\nCreateTimeMillis=" + createTimeMillis() +
",\nStartTimeMillis=" + startTimeMillis() +
",\nResumeTimeMillis=" + resumeTimeMillis() +
",\nviewVisibleToUserNanoTime=" + visibleTimeMillis() +
",\nstate=" + state +
'}';
}
}
public static class MetricWindowCallbackWrapper extends WindowCallbackWrapper {
private ActivityLifecycleMetric activityLifecycleMetric;
public MetricWindowCallbackWrapper(Window.Callback wrapped, ActivityLifecycleMetric activityLifecycleMetric) {
super(wrapped);
this.activityLifecycleMetric = activityLifecycleMetric;
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (activityLifecycleMetric.state == ActivityLifecycleMetric.STATE_POST_RESUMED && hasFocus) {
activityLifecycleMetric.viewVisibleToUserNanoTime = System.nanoTime();
activityLifecycleMetric.state = ActivityLifecycleMetric.STATE_VISIBLE;
}
}
}
}