/* * Copyright (C) 2012 uPhyca Inc. * * Base on previous work by * Copyright (C) 2006 The Android Open Source Project * * 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 com.uphyca.testing.app; import static org.junit.Assert.assertNotNull; import java.util.List; import android.app.Activity; import android.app.Application; import android.app.Instrumentation; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.os.Bundle; import android.os.IBinder; import com.uphyca.testing.Proxies; import com.uphyca.testing.proxy.android.app.ActivityProxy; import com.xtremelabs.robolectric.Robolectric; import com.xtremelabs.robolectric.shadows.ShadowApplication; public class RobolectricInstrumentation extends Instrumentation { private static final class ActivityWaiter { public final Intent intent; public Activity activity; public ActivityWaiter(Intent _intent) { intent = _intent; } } private ActivityProxy proxyOf(Activity activity) { return Proxies.proxyOf(activity); } private Context mAppContext; private final Object mSync = new Object(); private List<ActivityWaiter> mWaitingActivities; public RobolectricInstrumentation() { ShadowApplication application = Robolectric.getShadowApplication(); mAppContext = application.getApplicationContext(); } private final void validateNotAppThread() { // noop. } /* * (non-Javadoc) * * @see android.app.Instrumentation#getTargetContext() */ public Context getTargetContext() { return mAppContext; } /* * (non-Javadoc) * * @see android.app.Instrumentation#getContext() */ public Context getContext() { return mAppContext; } /* * (non-Javadoc) * * @see * android.app.Instrumentation#callActivityOnCreate(android.app.Activity, * android.os.Bundle) */ @Override public void callActivityOnCreate(Activity activity, Bundle icicle) { proxyOf(activity).performCreate(icicle); } /* * (non-Javadoc) * * @see * android.app.Instrumentation#callActivityOnDestroy(android.app.Activity) */ @Override public void callActivityOnDestroy(Activity activity) { proxyOf(activity).performDestroy(); } /* * (non-Javadoc) * * @see * android.app.Instrumentation#callActivityOnRestoreInstanceState(android * .app.Activity, android.os.Bundle) */ @Override public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState) { proxyOf(activity).performRestoreInstanceState(savedInstanceState); } /* * (non-Javadoc) * * @see * android.app.Instrumentation#callActivityOnPostCreate(android.app.Activity * , android.os.Bundle) */ @Override public void callActivityOnPostCreate(Activity activity, Bundle icicle) { proxyOf(activity).onPostCreate(icicle); } /* * (non-Javadoc) * * @see * android.app.Instrumentation#callActivityOnNewIntent(android.app.Activity, * android.content.Intent) */ @Override public void callActivityOnNewIntent(Activity activity, Intent intent) { proxyOf(activity).onNewIntent(intent); } /* * (non-Javadoc) * * @see * android.app.Instrumentation#callActivityOnStart(android.app.Activity) */ @Override public void callActivityOnStart(Activity activity) { proxyOf(activity).onStart(); } /* * (non-Javadoc) * * @see * android.app.Instrumentation#callActivityOnRestart(android.app.Activity) */ @Override public void callActivityOnRestart(Activity activity) { proxyOf(activity).onRestart(); } /* * (non-Javadoc) * * @see * android.app.Instrumentation#callActivityOnResume(android.app.Activity) */ @Override public void callActivityOnResume(Activity activity) { ActivityProxy delegate = proxyOf(activity); delegate.setMResumed(true); delegate.onResume(); } /* * (non-Javadoc) * * @see android.app.Instrumentation#callActivityOnStop(android.app.Activity) */ @Override public void callActivityOnStop(Activity activity) { proxyOf(activity).onStop(); } /* * (non-Javadoc) * * @see * android.app.Instrumentation#callActivityOnSaveInstanceState(android.app * .Activity, android.os.Bundle) */ @Override public void callActivityOnSaveInstanceState(Activity activity, Bundle outState) { proxyOf(activity).performSaveInstanceState(outState); } /* * (non-Javadoc) * * @see * android.app.Instrumentation#callActivityOnPause(android.app.Activity) */ @Override public void callActivityOnPause(Activity activity) { proxyOf(activity).performPause(); } /* * (non-Javadoc) * * @see * android.app.Instrumentation#callActivityOnUserLeaving(android.app.Activity * ) */ @Override public void callActivityOnUserLeaving(Activity activity) { proxyOf(activity).performUserLeaving(); } @Override public Activity startActivitySync(Intent intent) { validateNotAppThread(); synchronized (mSync) { intent = new Intent(intent); // ActivityInfo ai = // intent.resolveActivityInfo(getTargetContext().getPackageManager(), // 0); // if (ai == null) { // throw new RuntimeException("Unable to resolve activity for: " + // intent); // } // // String myProc = mThread.getProcessName(); // // if (!ai.processName.equals(myProc)) { // // // todo: if this intent is ambiguous, look here to see if // // // there is a single match that is in our package. // // throw new RuntimeException("Intent in process " + myProc + // // " resolved to different process " + ai.processName + ": " + // // intent); // // } // // intent.setComponent(new // ComponentName(ai.applicationInfo.packageName, ai.name)); // final ActivityWaiter aw = new ActivityWaiter(intent); // // if (mWaitingActivities == null) { // mWaitingActivities = new ArrayList(); // } // mWaitingActivities.add(aw); Activity activity = null; try { activity = (Activity) Class.forName(intent.getComponent() .getClassName()) .newInstance(); } catch (Exception e) { assertNotNull(activity); } Robolectric.shadowOf(activity).setIntent(intent); callActivityOnCreate(activity, null); callActivityOnStart(activity); callActivityOnResume(activity); return activity; // do { // try { // mSync.wait(); // } catch (InterruptedException e) { // } // } while (mWaitingActivities.contains(aw)); // // return aw.activity; } } @Override public void waitForIdleSync() { // noop. } @Override public void runOnMainSync(Runnable runner) { validateNotAppThread(); runner.run(); } @Override public Activity newActivity(Class<?> clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, Object lastNonConfigurationInstance) throws InstantiationException, IllegalAccessException { Activity activity = (Activity) clazz.newInstance(); proxyOf(activity).attach(context, null, this, token, application, intent, info, title, parent, id, lastNonConfigurationInstance, new Configuration()); return activity; } public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException { return (Activity) cl.loadClass(className) .newInstance(); } }