I love animations. They can be very useful when they are fast, visually pleasing, and serve a purpose. But when animations are slow, ugly, or do not make sense, they can literally ruin an experience.
I used to love Android’s screen-off animation in the CRT-era (from version 2.3 to 4.4), but also thought they were getting visually old, and it was time to change them.
Since Lollipop, the screen-off animation was changed to a fade-out, and I hated it so much.
The fade-out animation
Fade-outs are not necessarily bad. A fade-out can do a very good job in dismissing or hiding objects, especially when combined with other effects. But there are several problems with using it as a screen-off animation.
There’s a good reason why the CRT animation was so convincing: it’s familiar. Almost anyone looking at the animation would be familiar with it, because they probably saw it before in a similar situation and after doing the same action, i.e. turning off an old TV. But how fading-out is a convincing transition for turning the screen off?
It would have been ideal if the fade-out animation ran only after the screen times-out, in which case fade-outs make for a perfect pair with progressive decrease in brightness that usually happen while timing-out. But running a fade-out for turning-off a screen, and after clicking a hardware button? It just didn’t make sense to me.
The other problem may seem more personal, but I was running Lollipop on an old Galaxy Nexus, and the animation (the preparation step I would imagine1) was so resource-unfriendly, that my phone screen was freezing for almost a second every time I try to turn the screen off. This also resulted in the screen-off sound effect not being synchronized with the animation… and it was driving me nuts.
That’s why I decided to either build a new screen-off animation, or remove it entirely.
Looking for solutions
I didn’t find any custom ROM that can run on my phone and gives the option to change or disable the animation. So, I looked for DIY solutions.
The first option I thought of was to edit the animation in the source code of a custom ROM. Testing was a big downside with this method, considering I had to make a full build then flash it every time I wanted to test something. I didn’t have a previous experience with building ROMs, so this was only speculation.
So I looked for easier solutions, and that was when I rediscovered Xposed: the framework that lets you customize the system internally without doing a complete build. And that sounded perfect. At this point, I’d heard about Xposed before, but never thought I would need it.
I installed Xposed and started looking for available modules in its repository. I found one that allows changing the animation to pre-made ones, but not disable it.
The module was 3 years old, and suffered some performance issues. It even had a CRT-lookalike animation, but that’s when I knew I just want to disable the animation as a start. So there was no other way but to build a module myself.
I opened the Xposed guide and started reading it. The guide was very clear and detailed, and it even had tutorial-like examples that walk you very slowly into building your first module. So, kudos to the developers for that!
Building the Xposed module
The only trick while building an Xposed module is to know where to start, Java “method” level. So I had to look in system’s source code to find what method is responsible for starting the animation. And again, GitHub search comes to the rescue.
It didn’t take long to find a method named animateScreenStateChange
, with an argument named performScreenOffTransition
, and it couldn’t be any more obvious.
After some reboots, and switching back and forth between Android Studio and the Xposed guide, I managed to do it, and I couldn’t believe my eyes when I first saw it.
So, this is how the module works:
- Wait for system services to load.
- Get inside the class
DisplayPowerController
containing the method. - Wait for any call for the method
animateScreenStateChange
. - Block it from running.
- Get the parameters of the call, and modify the
performScreenOffTransition
parameter to always befalse
. - Call the method again with the modified parameters.
void handleLoadPackage(LoadPackageParam lpparam) {
// 1. Wait for system services to be loaded:
if (!lpparam.packageName.equals("android"))
return;
// (Log to tell we're in):
.log("[InstantScreenOff]: Ready.");
XposedBridge
.findAndHookMethod(
XposedHelpers// 2. Get inside the `DisplayPowerController` class:
.findClass("com.android.server.display.DisplayPowerController", /*...*/),
XposedHelpers// 3. Get inside the `animateScreenStateChange` method of the class:
"animateScreenStateChange", int.class, boolean.class,
new XC_MethodReplacement() {
// 4. Block original call of the method:
Object replaceHookedMethod(MethodHookParam param) {
// 5. Change `performScreenOffTransition` parameter to always be `false`:
.args[1] = false;
param// 6. Recall the original method with new parameters:
return XposedBridge.invokeOriginalMethod(param.method, /*...*/, param.args);
}
}
);
}
If you’re building a module, the Xposed project wiki on GitHub is a great start. Start with “Using the Xposed Framework API” then “Development tutorial”.
Also, here’s a quick tip I wish I knew faster:
To edit a system method that’s not part of a system app (as in our case), you must wait to hook the system services in the handleLoadPackage
method, using the package name android
instead of, e.g. com.android.systemui
or any other package (see line #3).
After this experience, I was convinced that no animation would give me the same satisfaction I get without animations at all. So I decided to keep it this way and not building a custom one.
I switched to a Marshmallow ROM after building this module, and there’s now a Nougat ROM for Galaxy Nexus (this phone doesn’t die), and the only reason I can’t switch to it yet is that Nougat is not supported by Xposed yet. I literally can’t go back.
It’s worth a mention that iOS also switched to fade-outs since iOS 7 (even before Lollipop), and it didn’t look any better to me.
I published the module’s source code on GitHub if you’re interested (start with AnimationDisabler.java
). And if you just want to use the module, it’s now available on the Xposed repository.
So yeah, Xposed is cool, and everything in Android is customizable without a complete ROM build.
Android is very slow in taking screenshots (at least on a 7-year-old device), for some reason. And while screen-off animations may look like they are running on the current view of the screen, they actually run on a screenshot taken just before the animation runs. You can test this by turning the screen off while making a fast movement (like scrolling), and you’ll notice the animation actually runs on a previous, still frame of the view.↩︎