Android Open Source - android-marvin Extended Activity Monitor






From Project

Back to project page android-marvin.

License

The source code is released under:

Apache License

If you think the Android project android-marvin listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright 2010 akquinet/*  ww w. java  2 s  .co  m*/
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.akquinet.android.marvin.monitor;

import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import android.app.Activity;
import android.app.Instrumentation;
import android.app.Instrumentation.ActivityMonitor;
import android.content.IntentFilter;
import android.util.Log;


public class ExtendedActivityMonitor
{
    private static final int CHECK_FOR_INTERRUPT_CYCLE_DURATION = 10000;
    
    private volatile boolean stopped = false;

    private Thread activityMonitorThread;
    private ActivityMonitor activityInstanceMonitor;

    /** holds all activity instances started during run of this monitor. */
    private List<StartedActivity> startedActivities =
            new LinkedList<StartedActivity>();

    /** holds listeners to be informed of certain activity starts */
    private Map<Class<? extends Activity>, List<ActivityStartListener>> activityStartListeners =
            new HashMap<Class<? extends Activity>, List<ActivityStartListener>>();

    public interface ActivityStartListener
    {
        void started(Activity activity);
    }

    public ExtendedActivityMonitor(Instrumentation instrumentation) {
        this(instrumentation, null);
    }

    public ExtendedActivityMonitor(Instrumentation instrumentation,
            IntentFilter filter) {
        // create our monitor
        activityInstanceMonitor = instrumentation.addMonitor(
                filter, null, false);

        this.activityMonitorThread = new Thread() {
            @Override
            public void run() {
                ThreadPoolExecutor executor =
                        new ThreadPoolExecutor(2, 5, 5, TimeUnit.SECONDS,
                                new ArrayBlockingQueue<Runnable>(5));

                while (true) {
                    /*
                     * ActivityMonitor's waiting is non-interruptable (who
                     * knows, why), so we check ourselves for interruption on a
                     * regular basis.
                     */
                    Activity activity = activityInstanceMonitor
                            .waitForActivityWithTimeout(CHECK_FOR_INTERRUPT_CYCLE_DURATION);
                    long startTime = System.currentTimeMillis();

                    if (activity != null) {
                        int activitiesCount = startedActivities.size();
                        if (activitiesCount > 0
                                && startedActivities.get(activitiesCount - 1).getActivity() == activity) {
                            continue;
                        }

                        // ok we got an activity, save the instance
                        synchronized (startedActivities) {
                            startedActivities.add(new StartedActivity(activity,
                                    startTime));
                        }
                        Log.i(getClass().getSimpleName(),
                                "Activity start: " + activity.getClass().getName());
                        // inform listeners within another thread
                        executor.submit(new ActivityStartListenerUpdater(
                                activity));
                    }
                    if (interrupted() || stopped) {
                        executor.shutdown();
                        // We were interrupted, stop waiting for new activites.
                        return;
                    }
                }
            }
        };
    }

    public void start() {
        this.activityMonitorThread.start();
        stopped = false;
    }

    /**
     * Returns the most recently started {@link Activity}. In most cases, this
     * will be the activity currently visible on screen.
     */
    public Activity getMostRecentlyStartedActivity() {
        List<StartedActivity> activities = getStartedActivities();

        if (activities.size() == 0) {
            return null;
        }

        return activities.get(activities.size() - 1).getActivity();
    }

    public List<StartedActivity> getStartedActivities() {
        List<StartedActivity> result = new ArrayList<StartedActivity>();
        
        synchronized (startedActivities) {
            Iterator<StartedActivity> iter = startedActivities.iterator();
            while (iter.hasNext()) {
                StartedActivity activity = iter.next();
                if (activity.getActivity().isFinishing()) {
                    iter.remove();
                }
                else {
                    result.add(activity);
                }
            }

            return new LinkedList<StartedActivity>(startedActivities);
        }
    }

    /**
     * Blocks until an {@link Activity} of the given type is started. The
     * instance of the started activity is then returned. If such an activity is
     * not started within the given amount of milliseconds, returns null.
     * 
     * @param activityClass
     *            the type of activity to wait for
     * @param timeout
     *            amount to wait for activity start
     * @param timeUnit
     *            the unit of the timeout parameter
     * @return the activity waited for, or null if timeout was reached before
     *         any suitable activity was started
     */
    @SuppressWarnings("unchecked")
    public final <T extends Activity> T waitForActivity(
            Class<T> activityClass, long timeout, TimeUnit timeUnit) {
        long timeoutInMs = timeUnit.toMillis(timeout);

        Activity mostRecentlyStartedActivity = getMostRecentlyStartedActivity();
        if (mostRecentlyStartedActivity != null
                && mostRecentlyStartedActivity.getClass().equals(
                        activityClass)) {
            return (T) mostRecentlyStartedActivity;
        }

        int startedActivitiesCount = startedActivities.size();
        if (startedActivitiesCount > 0) {
            StartedActivity startedActivity = startedActivities.get(startedActivitiesCount - 1);
            if (startedActivity.getActivity().getClass().equals(activityClass)) {
                return (T) startedActivity.getActivity();
            }
        }

        // we need some kind of container to be shared between two threads
        final List<T> activityContainer = new LinkedList<T>();

        // register the listener, we are now informed when the activity starts
        registerActivityStartListener(activityClass,
                new ActivityStartListener() {
                    public void started(Activity activity) {
                        synchronized (activityContainer) {
                            // OK, the activity has been started. Put its
                            // instance in the container and notify.
                            activityContainer.add((T) activity);
                            activityContainer.notifyAll();
                        }
                    }
                });

        // Now, wait for the activity start by waiting on the container object.
        synchronized (activityContainer) {
            try {
                long time = System.currentTimeMillis();
                while (activityContainer.isEmpty()) {
                    activityContainer.wait(2000);
                    if (System.currentTimeMillis() - time > timeoutInMs) {
                        return null;
                    }
                }
            }
            catch (InterruptedException e) {
            }

            if (activityContainer.size() > 0) {
                return activityContainer.get(0);
            }
        }

        /*
         * Container was empty, we were either interrupted or more likely, the
         * timeout was reached. Return null.
         */
        return null;
    }

    public void stop() {
        stopped = true;
        activityMonitorThread.interrupt();
    }

    public void clear() {
        synchronized (startedActivities) {
            startedActivities.clear();
            activityStartListeners.clear();
        }
    }

    /*
     * Not synchronized, do that in the caller code.
     */
    private List<ActivityStartListener> getActivityStartListeners(
            Class<? extends Activity> activityClass) {
        List<ActivityStartListener> result =
                this.activityStartListeners.get(activityClass);
        if (result == null) {
            result = new ArrayList<ActivityStartListener>();
            this.activityStartListeners.put(activityClass, result);
        }
        return result;
    }

    private void registerActivityStartListener(
            Class<? extends Activity> activityClass,
            ActivityStartListener listener) {
        synchronized (this.activityStartListeners) {
            List<ActivityStartListener> listeners =
                    getActivityStartListeners(activityClass);
            listeners.add(listener);
        }
    }

    class ActivityStartListenerUpdater implements Callable<Void>
    {
        private final Activity activity;

        public ActivityStartListenerUpdater(Activity activity) {
            this.activity = activity;
        }

        public Void call() {
            synchronized (activityStartListeners) {
                List<ActivityStartListener> listeners =
                        getActivityStartListeners(
                        activity.getClass());
                for (ActivityStartListener listener : listeners) {
                    listener.started(activity);
                }
            }
            return null;
        }
    };
}




Java Source Code List

de.akquinet.android.marvin.ActivityTestCase.java
de.akquinet.android.marvin.AndroidMatchers.java
de.akquinet.android.marvin.AndroidTestCase.java
de.akquinet.android.marvin.ServiceTestCase.java
de.akquinet.android.marvin.actions.ActionFactory.java
de.akquinet.android.marvin.actions.ActivityAction.java
de.akquinet.android.marvin.actions.AwaitAction.java
de.akquinet.android.marvin.actions.BaseAction.java
de.akquinet.android.marvin.actions.FindViewAction.java
de.akquinet.android.marvin.actions.PerformAction.java
de.akquinet.android.marvin.actions.ViewAction.java
de.akquinet.android.marvin.actions.ViewFilter.java
de.akquinet.android.marvin.matchers.Condition.java
de.akquinet.android.marvin.matchers.HasText.java
de.akquinet.android.marvin.matchers.IsEnabled.java
de.akquinet.android.marvin.matchers.IsOnScreen.java
de.akquinet.android.marvin.matchers.IsVisible.java
de.akquinet.android.marvin.matchers.ViewGroupComparison.java
de.akquinet.android.marvin.matchers.util.WaitForConditionUtil.java
de.akquinet.android.marvin.monitor.ExtendedActivityMonitor.java
de.akquinet.android.marvin.monitor.StartedActivity.java
de.akquinet.android.marvin.util.MarvinConnectivityUtil.java
de.akquinet.android.marvin.util.TemporaryServiceConnection.java