Skip to content

Instantly share code, notes, and snippets.

@riggaroo
Created April 13, 2016 17:31
Show Gist options
  • Save riggaroo/7f1e6cd4a52c61920b564c6465d1f1d9 to your computer and use it in GitHub Desktop.
Save riggaroo/7f1e6cd4a52c61920b564c6465d1f1d9 to your computer and use it in GitHub Desktop.
Custom Android Espresso Test Runner - Unlocking a Device, Granting Permission to turn animations off, turning the Screen on.
<?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="org.bookdash.android">
<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<!-- 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>
// Grant animation permissions to avoid test failure because of ui sync.
task grantAnimationPermissions(type: Exec, dependsOn: 'installMock') {
group = 'test'
description = 'Grant permissions for testing.'
def absolutePath = file('..') // Get project absolute path
commandLine "$absolutePath/set_animation_permissions.sh org.bookdash.android".split(" ")
}
// Source: http://stackoverflow.com/q/29908110/112705
afterEvaluate {
// 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('assembleMockAndroidTest')) {
task.dependsOn grantAnimationPermissions
}
}
}
/**
* Tests can fail for other reasons than code, it´ because of the animations and espresso sync and
* emulator state (screen off or locked)
* <p/>
* Before all the tests prepare the device to run tests and avoid these problems.
* <p/>
* - Disable animations
* - Disable keyguard lock
* - Set it to be awake all the time (dont let the processor sleep)
*
* @see <a href="u2020 open source app by Jake Wharton">https://github.com/JakeWharton/u2020</a>
* @see <a href="Daj gist">https://gist.github.com/daj/7b48f1b8a92abf960e7b</a>
*/
public final class CustomTestRunner extends AndroidJUnitRunner {
private static final String TAG = "CustomTestRunner";
@Override
public void onStart() {
runOnMainSync(new Runnable() {
@Override
public void run() {
Context app = CustomTestRunner.this.getTargetContext().getApplicationContext();
CustomTestRunner.this.disableAnimations(app);
String name = CustomTestRunner.class.getSimpleName();
unlockScreen(app, name);
keepSceenAwake(app, name);
}
});
super.onStart();
}
@Override
public void finish(int resultCode, Bundle results) {
super.finish(resultCode, results);
enableAnimations(getContext());
}
private void keepSceenAwake(Context app, String name) {
PowerManager power = (PowerManager) app.getSystemService(Context.POWER_SERVICE);
power.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, name)
.acquire();
}
private void unlockScreen(Context app, String name) {
KeyguardManager keyguard = (KeyguardManager) app.getSystemService(Context.KEYGUARD_SERVICE);
keyguard.newKeyguardLock(name).disableKeyguard();
}
void disableAnimations(Context context) {
int permStatus = context.checkCallingOrSelfPermission(Manifest.permission.SET_ANIMATION_SCALE);
if (permStatus == PackageManager.PERMISSION_GRANTED) {
setSystemAnimationsScale(0.0f);
}
}
void enableAnimations(Context context) {
int permStatus = context.checkCallingOrSelfPermission(Manifest.permission.SET_ANIMATION_SCALE);
if (permStatus == PackageManager.PERMISSION_GRANTED) {
setSystemAnimationsScale(1.0f);
}
}
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});
Log.d(TAG, "Changed permissions of animations");
} catch (Exception e) {
Log.e(TAG, "Could not change animation scale to " + animationScale + " :'(");
}
}
}
#!/bin/bash
#
# source https://github.com/zielmicha/adb-wrapper
#
# argument: apk package
# Set permission android.permission.SET_ANIMATION_SCALE for each device.
# ex: sh set_animation_permissions.sh <package>
#
adb=$ANDROID_HOME/platform-tools/adb
package=$1
if [ "$#" = 0 ]; then
echo "No parameters found, run with sh set_animation_permissions.sh <package>"
exit 0
fi
# get all the devices
devices=$($adb devices | grep -v 'List of devices' | cut -f1 | grep '.')
for device in $devices; do
echo "Setting permissions to device" $device "for package" $package
$adb -s $device shell pm grant $package android.permission.SET_ANIMATION_SCALE
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment