package com.google.android.apps.common.testing.testrunner; import static com.google.android.apps.common.testing.testrunner.util.Checks.checkNotNull; import android.app.Activity; import android.os.Looper; import android.util.Log; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; final class ActivityLifecycleMonitorImpl implements ActivityLifecycleMonitor { private static final String TAG = "LifecycleMonitor"; private final boolean declawThreadCheck; ActivityLifecycleMonitorImpl() { this(false); } //For Testing ActivityLifecycleMonitorImpl(boolean declawThreadCheck) { this.declawThreadCheck = declawThreadCheck; } // Accessed from any thread. private List<WeakReference<ActivityLifecycleCallback>> callbacks = new ArrayList<WeakReference<ActivityLifecycleCallback>>(); // Only accessed on main thread. private List<ActivityStatus> activityStatuses = new ArrayList<ActivityStatus>(); @Override public void addLifecycleCallback(ActivityLifecycleCallback callback) { // there will never be too many callbacks, so iterating over a list will probably // be faster then the constant time costs of setting up and maintaining a map. checkNotNull(callback); synchronized (callbacks) { boolean needsAdd = true; Iterator<WeakReference<ActivityLifecycleCallback>> refIter = callbacks.iterator(); while (refIter.hasNext()) { ActivityLifecycleCallback storedCallback = refIter.next().get(); if (null == storedCallback) { refIter.remove(); } else if (storedCallback == callback) { needsAdd = false; } } if (needsAdd) { callbacks.add(new WeakReference<ActivityLifecycleCallback>(callback)); } } } @Override public void removeLifecycleCallback(ActivityLifecycleCallback callback) { checkNotNull(callback); synchronized (callbacks) { Iterator<WeakReference<ActivityLifecycleCallback>> refIter = callbacks.iterator(); while (refIter.hasNext()) { ActivityLifecycleCallback storedCallback = refIter.next().get(); if (null == storedCallback) { refIter.remove(); } else if (storedCallback == callback) { refIter.remove(); } } } } @Override public Stage getLifecycleStageOf(Activity activity) { checkMainThread(); checkNotNull(activity); Iterator<ActivityStatus> statusIterator = activityStatuses.iterator(); while (statusIterator.hasNext()) { ActivityStatus status = statusIterator.next(); Activity statusActivity = status.activityRef.get(); if (null == statusActivity) { statusIterator.remove(); } else if (activity == statusActivity) { return status.lifecycleStage; } } throw new IllegalArgumentException("Unknown activity: " + activity); } @Override public Collection<Activity> getActivitiesInStage(Stage stage) { checkMainThread(); checkNotNull(stage); List<Activity> activities = new ArrayList<Activity>(); Iterator<ActivityStatus> statusIterator = activityStatuses.iterator(); while (statusIterator.hasNext()) { ActivityStatus status = statusIterator.next(); Activity statusActivity = status.activityRef.get(); if (null == statusActivity) { statusIterator.remove(); } else if (stage == status.lifecycleStage) { activities.add(statusActivity); } } return activities; } void signalLifecycleChange(Stage stage, Activity activity) { // there are never too many activities in existance in an application - so we keep // track of everything in a single list. Log.d(TAG, "Lifecycle status change: " + activity + " in: " + stage); boolean needsAdd = true; Iterator<ActivityStatus> statusIterator = activityStatuses.iterator(); while (statusIterator.hasNext()) { ActivityStatus status = statusIterator.next(); Activity statusActivity = status.activityRef.get(); if (null == statusActivity) { statusIterator.remove(); } else if (activity == statusActivity) { needsAdd = false; status.lifecycleStage = stage; } } if (needsAdd) { activityStatuses.add(new ActivityStatus(activity, stage)); } synchronized (callbacks) { Iterator<WeakReference<ActivityLifecycleCallback>> refIter = callbacks.iterator(); while (refIter.hasNext()) { ActivityLifecycleCallback callback = refIter.next().get(); if (null == callback) { refIter.remove(); } else { try { Log.d(TAG, "running callback: " + callback); callback.onActivityLifecycleChanged(activity, stage); Log.d(TAG, "callback completes: " + callback); } catch (RuntimeException re) { Log.e( TAG, String.format( "Callback threw exception! (callback: %s activity: %s stage: %s)", callback, activity, stage), re); } } } } } private void checkMainThread() { if (declawThreadCheck) { return; } if (!Thread.currentThread().equals(Looper.getMainLooper().getThread())) { throw new IllegalStateException("Querying activity state off main thread is not allowed."); } } private static class ActivityStatus { private final WeakReference<Activity> activityRef; private Stage lifecycleStage; ActivityStatus(Activity activity, Stage stage) { this.activityRef = new WeakReference<Activity>(checkNotNull(activity)); this.lifecycleStage = checkNotNull(stage); } } }