Skip to content

Instantly share code, notes, and snippets.

@pt2121
Forked from xrigau/AndroidManifest.xml
Created September 29, 2015 22:32
Show Gist options
  • Save pt2121/e6d0e071e3b3d0835407 to your computer and use it in GitHub Desktop.
Save pt2121/e6d0e071e3b3d0835407 to your computer and use it in GitHub Desktop.
Disable animations for Espresso tests - run with `gradle cATDD`
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.novoda.espresso">
<!-- For espresso testing purposes, this is removed in live builds, but not in dev builds -->
<uses-permission android:name="android.permission.SET_ANIMATION_SCALE" />
<!-- ... -->
</manifest>
apply plugin: 'android'
android {
// ...
productFlavors {
dev {
// The one for development/testing
}
live {
// The flavour for releasing
}
}
}
// ...
task grantAnimationPermission(type: Exec, dependsOn: 'installDevDebug') { // or install{productFlavour}{buildType}
commandLine "adb shell pm grant $android.defaultConfig.packageName android.permission.SET_ANIMATION_SCALE".split(' ')
}
tasks.whenTaskAdded { task ->
if (task.name.startsWith('connectedAndroidTest')) {
task.dependsOn grantAnimationPermission
}
}
def copyAndReplaceText(source, dest, Closure replaceText) {
dest.write(replaceText(source.text))
}
// Override Data in Manifest - This can be done using different Manifest files for each flavor, this way there's no need to modify the manifest
android.applicationVariants.all { variant ->
if (variant.name.startsWith('dev')) { // Where dev is the one you'll use to run Espresso tests
System.out.println("Not removing the SET_ANIMATION_SCALE permission for $variant.name")
return
}
System.out.println("Removing the SET_ANIMATION_SCALE permission for $variant.name")
variant.processManifest.doLast {
copyAndReplaceText(manifestOutputFile, manifestOutputFile) {
def replaced = it.replace('<uses-permission android:name="android.permission.SET_ANIMATION_SCALE"/>', '');
if (replaced.contains('SET_ANIMATION_SCALE')) {
// For security, imagine an extra space is added before closing tag, then the replace would fail - TODO use regex
throw new RuntimeException("Don't ship with this permission! android.permission.SET_ANIMATION_SCALE")
}
replaced
}
}
}
public class MyInstrumentationTestCase extends ActivityInstrumentationTestCase2<MyActivity> {
private SystemAnimations systemAnimations;
public MyInstrumentationTestCase() {
super(MyActivity.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
systemAnimations = new SystemAnimations(getInstrumentation().getContext());
systemAnimations.disableAll();
getActivity();
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
systemAnimations.enableAll();
}
}
class SystemAnimations {
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