Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save ardock/3d02da5db42943b10734 to your computer and use it in GitHub Desktop.
Save ardock/3d02da5db42943b10734 to your computer and use it in GitHub Desktop.
The set of changes required to disable animations any time Espresso tests run. These instructions will only work on emulators and on rooted devices. This is based on the instructions in: https://code.google.com/p/android-test-kit/wiki/DisablingAnimations
<?xml version="1.0" encoding="utf-8"?>
<!-- Put this file in the "debug" folder so it only gets merged into debug builds -->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cookbrite">
<!-- Disable animations on debug builds so that the animations do not interfere with Espresso
tests. Adding this permission to the manifest is not sufficient - you must also grant the
permission over adb! -->
<uses-permission android:name="android.permission.SET_ANIMATION_SCALE" />
</manifest>
public abstract class BaseStatelessBlackBoxEspressoTest<T extends Activity> extends ActivityInstrumentationTestCase2<T> {
private SystemAnimations mSystemAnimations;
public BaseStatelessBlackBoxEspressoTest(Class clazz) {
super(clazz);
}
@Override
protected void setUp() throws Exception {
super.setUp();
mSystemAnimations = new SystemAnimations(getInstrumentation().getContext());
mSystemAnimations.disableAll();
getActivity();
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
// If you want to test this entire Gist is working, comment out this line and
// run a test. Then check these options in Settings -> Developer:
// -Window animation scale
// -Transition animation scale
// -Animator duration scale
// They should all show "Animation off". Then re-enable this line and run a
// test again, this time it will show "Animation scale 1x"
mSystemAnimations.enableAll();
}
}
apply plugin: 'android'
android {
productFlavors {
dev {
applicationId "com.cookbrite.dev"
}
production {
applicationId "com.cookbrite.android"
}
}
// ...
// Get the path to ADB. Required when running tests directly from Android Studio.
// Source: http://stackoverflow.com/a/26771087/112705
def adb = android.getAdbExe().toString()
// Source: http://stackoverflow.com/q/29908110/112705
afterEvaluate {
task grantAnimationPermissionDev(type: Exec, dependsOn: 'installDevDebug') {
commandLine "$adb shell pm grant $android.productFlavors.dev.applicationId android.permission.SET_ANIMATION_SCALE".split(' ')
}
task grantAnimationPermissionProduction(type: Exec, dependsOn: 'installProductionDebug') {
commandLine "$adb shell pm grant $android.productFlavors.production.applicationId android.permission.SET_ANIMATION_SCALE".split(' ')
}
// When launching individual tests from Android Studio, it seems that only the assemble tasks
// get called directly, not the install* versions
tasks.each { task ->
if (task.name.startsWith('assembleDevDebugAndroidTest')) {
task.dependsOn grantAnimationPermissionDev
} else if (task.name.startsWith('assembleProductionDebugAndroidTest')) {
task.dependsOn grantAnimationPermissionProduction
}
}
}
/**
* Disable animations so that they do not interfere with Espresso tests.
*
* Source: https://code.google.com/p/android-test-kit/wiki/DisablingAnimations
*/
public final class SystemAnimations extends AndroidJUnitRunner {
private static final String ANIMATION_PERMISSION = "android.permission.SET_ANIMATION_SCALE";
private static final float DISABLED = 0.0f;
private static final float DEFAULT = 1.0f;
private final Context context;
SystemAnimations(Context context) {
this.context = context;
}
void disableAll() {
int permStatus = context.checkCallingOrSelfPermission(ANIMATION_PERMISSION);
if (permStatus == PackageManager.PERMISSION_GRANTED) {
setSystemAnimationsScale(DISABLED);
}
}
void enableAll() {
int permStatus = context.checkCallingOrSelfPermission(ANIMATION_PERMISSION);
if (permStatus == PackageManager.PERMISSION_GRANTED) {
setSystemAnimationsScale(DEFAULT);
}
}
private void setSystemAnimationsScale(float animationScale) {
try {
Class<?> windowManagerStubClazz = Class.forName("android.view.IWindowManager$Stub");
Method asInterface = windowManagerStubClazz.getDeclaredMethod("asInterface", IBinder.class);
Class<?> serviceManagerClazz = Class.forName("android.os.ServiceManager");
Method getService = serviceManagerClazz.getDeclaredMethod("getService", String.class);
Class<?> windowManagerClazz = Class.forName("android.view.IWindowManager");
Method setAnimationScales = windowManagerClazz.getDeclaredMethod("setAnimationScales", float[].class);
Method getAnimationScales = windowManagerClazz.getDeclaredMethod("getAnimationScales");
IBinder windowManagerBinder = (IBinder) getService.invoke(null, "window");
Object windowManagerObj = asInterface.invoke(null, windowManagerBinder);
float[] currentScales = (float[]) getAnimationScales.invoke(windowManagerObj);
for (int i = 0; i < currentScales.length; i++) {
currentScales[i] = animationScale;
}
setAnimationScales.invoke(windowManagerObj, new Object[]{currentScales});
} catch (Exception e) {
Log.e("SystemAnimations", "Could not change animation scale to " + animationScale + " :'(");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment