After I posted the Pixel Navigation bar animation mod for Nexus 5X / Nexus 6P, I have received a number of people’s messages, they were asking me to port the animation to their Roms. So I have tried on some custom Rom and succeed in 7.1 Rom, like Oneplus One with AOSP Rom, HTC M8 with CM 14.1, and Nexus 6 running UBERSTOCK Rom, but failed with all the 7.0 Roms, maybe someone could pointed out if the failure is API related.
Meanwhile some theme/mod developers wanted to know about the trick, and ask me if they can plant the animation in their mod, I said yes and I’m going to record these words to show how to modify the System UI component to add the Pixel exclusive navigation bar animation.
First of all, to clarify, the navigation bar animation was just a animation, it was connected with Google Assistant by default, that means you only can use it in English/Germany at present, not available in other languages. That’s why in some Pixel review videos, you did not see this little creativity things. But I have succeed in separating it from Google Assistant connection, and now it can perform even if without the Google Assistant, I hope these animation can be show in most of 7.1 Roms soon.
PS: Android 7.1.1 Final ‘s update was added at Step 3.4, new code. Pixel’s apk still the old one, will upload the latest 7.1.1 version later.
This is a guide for people who know about Windows’s CMD commands and have some base code skills. For other newbies, I will go to explain everything in a simple way and will take the Nexus 5X for an example, we will do this on Windows.
Tools we need to download:
JDK, for Java tools runtime, you can get it from oracle.com, install it then reboot your PC.
Baksmali, for decode SystenUI.odex, you can get it from https://bitbucket.org/JesusFreke/smali/downloads, download and put the Baksmali.jar file in a new folder named “Baksmali”.
Apktool, for decode SystemUI.apk, get it from https://ibotpeaches.github.io/Apktool/, download and put the Apktool.jar, Apktool.bat in a new folder “Apktool”.
Sublime Text, for searching and editing smail file, download it in their official website.
Web also need to get those files from your Rom:
SystemUI.apk, Path: \Root\System\priv-app\SystemUI\
SystemUI.odex (Stock Rom Only), Path: \Root\System\priv-app\SystemUI\ota\arm64\(/arm for 32 bit device.)
framework-res.apk Path: \Root\System\framework\
Oat files, required to deodex process.(Stock Rom only): Path: \Root\System\framework\arm64\(/arm for 32 bit device), PS. need all files in the directory.
We also need the SystemUIGoogle.apk form Pixel or Pixel XL (for different dip screen), we need their resources and smali codes. Download it here (All has updated to 7.1.1):
Pixel: SystemUIGoogle.apk from AFH mirror
Pixel XL: SystemUIGoogle XL.apk from AFH mirror
Update 02/13/2017: This Guide still works for Android 7.1.2 beta.
OK, I suppose you already get all stuff, let’s begin the magic. Remember I am using the Nexus 5X with Stock 7.1.1 developer preview 1 as example, things will change on your side with different devices, do not copy and paste all things from here to your files.
Step 1, Stock Rom was odexed, (Custom Rom does not have this issue, jump to step 2 if you are on custom Roms like CM or AOSP rom.) it means the front-end and back-end of system apps were separated in two files, like SystemUI.apk and SystemUI.odex, we need to combine the two files together.
How to combine the apk and odex file?
If you search on the Internet for how to decompile apk files, you can find many resources, learn about this knowledge, then, let’s do the easy steps first, which is decompile SystemUI.apk. In Windows’s explorer, navigate to the Apktool folder, hold shift key and right click the mouse, select “Open Command Window Here”, this will open a black DOS command window. Copy framework-res.apk, SystemUI.apk to the same folder.
We need to install the framework for Apktool to decompile apks. Input the command in the DOS command window.
java -jar apktool.jar if framework-res.apk
Wait a few seconds, apktool will response you a successful message. Then let’s input the decompile command,
java -jar apktool.jar d SystemUI.apk
, then we wait for apktool finish decompile the SystemUI.apk and get the /SystemUI folder, look into it and you can find “original”, “res”, two sun-folders, we are missing the “smali” folder. Where is it? It is inside the SystemUI.odex.
Step 1.1 Let’s go to the Baksmali folder. We put the SystemUI.odex and the Oat files all together in the root of “Baksmali” folder, open DOS command here and input this command:
java -jar baksmali.jar deodex SystemUI.odex -l
, a “out” folder will appear in /Baksmali folder, open it and copy all files, then go back to “SystemUI” folder, create a folder named it “smali” , open it then paste the files from “out”, until now, we get all the sources for compile a new SystemUI.apk, but we need to modify something before the first compilation (we have to compile it twice.)
Step 2 Plant the new resources of Pixel navigation bar animation from SystemUIGoogle.apk. First, we decompile the SystemUIGoogle.apk with Apktool, we got the folder SystemUIGoogle, now I will give you a list for telling you what we have to copy and modify, go into SystemUIGoogle folder, copy those files to SystenUI folder with the same path “\res\drawable\”.
\res\drawable\halo.xml
\res\drawable\ic_sysbar_opa_blue.xml
\res\drawable\ic_sysbar_opa_green.xml
\res\drawable\ic_sysbar_opa_red.xml
\res\drawable\ic_sysbar_opa_yellow.xml
Good, you should be familiar with the operation of apktool, next, we will modify some xml files for making the new resources alive. I also make a list for the modification details, all the operation has to be done with Sublime Text:
Step 2.1. Open SystemUI\res\layout\home.xml and SystemUIGoogle\res\layout\home.xml with Sublime Text.
Change
<com.android.systemui.statusbar.policy.KeyButtonView android:id="@id/home" android:layout_width="@dimen/navigation_key_width" android:layout_height="fill_parent" android:src="@drawable/ic_sysbar_home" android:scaleType="center" android:layout_weight="0.0" android:contentDescription="@string/accessibility_home" android:paddingStart="@dimen/navigation_key_padding" android:paddingEnd="@dimen/navigation_key_padding" systemui:keyCode="3"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" />
To
<com.google.android.systemui.OpaLayout android:id="@id/home" android:clipChildren="false" android:clipToPadding="false" android:layout_width="@dimen/navigation_key_width" android:layout_height="fill_parent" android:layout_weight="0.0" android:paddingStart="@dimen/navigation_key_padding" android:paddingEnd="@dimen/navigation_key_padding"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto">
<RelativeLayout android:layout_width="fill_parent" android:layout_height="fill_parent">
<ImageView android:id="@id/red" android:src="@drawable/ic_sysbar_opa_red" android:importantForAccessibility="no" style="@style/DotStyle" />
<ImageView android:id="@id/blue" android:src="@drawable/ic_sysbar_opa_blue" android:importantForAccessibility="no" style="@style/DotStyle" />
<ImageView android:id="@id/green" android:src="@drawable/ic_sysbar_opa_green" android:importantForAccessibility="no" style="@style/DotStyle" />
<ImageView android:id="@id/yellow" android:src="@drawable/ic_sysbar_opa_yellow" android:importantForAccessibility="no" style="@style/DotStyle" />
</RelativeLayout>
<ImageView android:layout_gravity="center" android:id="@id/white" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_sysbar_home" android:importantForAccessibility="no" />
<com.android.systemui.statusbar.policy.KeyButtonView android:layout_gravity="center" android:id="@id/home_button" android:layout_width="@dimen/navigation_key_width" android:layout_height="fill_parent" android:scaleType="center" android:contentDescription="@string/accessibility_home" systemui:keyCode="3" />
<ImageView android:layout_gravity="center" android:id="@id/halo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/halo" android:importantForAccessibility="no" />
</com.google.android.systemui.OpaLayout>
PS. copy the sources code from SystemUIGoogle’ home.xml rather than from here! You can copy all codes from SystemUIGoogle’ home.xml.
Step 2.2. Open SystemUI\res\values\dimens.xml, Copy these code from SystemUIGoogle’s dimens.xml, add it to the end of the file before “</resources> ” label.Y
<dimen name="halo_inner_radius">10.0dip</dimen>
<dimen name="halo_thickness">1.0dip</dimen>
<dimen name="halo_diameter">22.0dip</dimen>
<dimen name="opa_dot_diam">10.0dip</dimen>
<dimen name="opa_diamond_translation">16.0dip</dimen>
<dimen name="opa_line_y_translation">16.0dip</dimen>
<dimen name="opa_line_x_trans_ry">15.0dip</dimen>
<dimen name="opa_line_x_trans_bg">30.0dip</dimen>
<dimen name="opa_line_x_collapse_bg">46.0dip</dimen>
<dimen name="opa_line_x_collapse_ry">15.0dip</dimen>
<dimen name="opa_overshoot_translation">8.0dip</dimen>
<dimen name="opa_return_translation">-1.0dip</dimen>
<dimen name="opa_rest_to_collapse">7.0dip</dimen>
Step 2.3. Open SystemUI\res\values\id.xml, Copy these codes from SystemUIGoogle’s id.xml, add it to the end of the file before “</resources> ” label.
<item type="id" name="red">false</item>
<item type="id" name="blue">false</item>
<item type="id" name="green">false</item>
<item type="id" name="yellow">false</item>
<item type="id" name="white">false</item>
<item type="id" name="home_button">false</item>
<item type="id" name="halo">false</item>
Step 2.4. Open SystemUI\res\values\styles.xml, Copy the codes from SystemUIGoogle’s styles .xml, add it to the end of the file before “</resources> ” label.
<style name="DotStyle">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_centerHorizontal">true</item>
<item name="android:layout_centerVertical">true</item>
</style>
Step 2.5. Open SystemUI\res\xml\tuner_prefs.xml,
First, check your tuner_prefs.xml if it have a line contain fragment value: com.android.systemui.tuner.NavBarTuner, if so, this step should be bypass.
Or if your Rom already have a built-in navigation bar setting menu (like CM14, etc.), this Step 2.5 should be bypass.
If not, add this line of code:
<Preference android:title="@string/nav_bar" android:key="nav_bar" android:fragment="com.android.systemui.tuner.NavBarTuner" />
before this line (see below):
<Preference android:title="@string/other" android:key="other" android:fragment="com.android.systemui.tuner.OtherPrefs" />
If you can not find the line above, I mean the line contain “com.android.systemui.tuner.OtherPrefs” , then the step 2.5 should be bypass.
Double check there is no repeated lines in tuner_prefs.xml.
Step 2.6 Alright, we have do all the resources porting job, right now we are going to compile SystemUI.apk for the first time .
CMD window of Apktool, input command:
java -jar apktool.jar b SystemUI -c
Do Not forget to add the parameter -c as we need to keep the original signature, Why? because all the system file must have the same signature or the system will refuse to boot, and we must sign apk file after compilation but we didn’t have the original certification for signing, with -c parameter we can keep the signature unchanged.
The new SystemUI.apk will be present in “\dist” folder, copy the fresh SystemUI.apk to replace the one in \Apktool folder, then rename the “SystemUI” folder to “SystemUI-org” for backup. Then we will decompile the new SystemUI.apk one more time.
Step 3. The compilation will generate the public id for new resources we just added to “\res”, we need those specific ids to rebuild the connection between resources and the Pixel animation controller. We need to find those resources ids.
Step 3.1 Open \SystemUIGoogle\res\value\public.xml file in Sublime Text, Ctrl+F to open search box, find these lines, they are separated in 4 parts. There are:
Dimen
<public type="dimen" name="halo_inner_radius" id="0x7f1002a1" />
<public type="dimen" name="halo_thickness" id="0x7f1002a2" />
<public type="dimen" name="halo_diameter" id="0x7f1002a3" />
<public type="dimen" name="opa_dot_diam" id="0x7f1002a4" />
<public type="dimen" name="opa_diamond_translation" id="0x7f1002a5" />
<public type="dimen" name="opa_line_y_translation" id="0x7f1002a6" />
<public type="dimen" name="opa_line_x_trans_ry" id="0x7f1002a7" />
<public type="dimen" name="opa_line_x_trans_bg" id="0x7f1002a8" />
<public type="dimen" name="opa_line_x_collapse_bg" id="0x7f1002a9" />
<public type="dimen" name="opa_line_x_collapse_ry" id="0x7f1002aa" />
<public type="dimen" name="opa_overshoot_translation" id="0x7f1002ab" />
<public type="dimen" name="opa_return_translation" id="0x7f1002ac" />
<public type="dimen" name="opa_rest_to_collapse" id="0x7f1002ad" />
ID
<public type="id" name="red" id="0x7f1200c9" />
<public type="id" name="blue" id="0x7f1200ca" />
<public type="id" name="green" id="0x7f1200cb" />
<public type="id" name="yellow" id="0x7f1200cc" />
<public type="id" name="white" id="0x7f1200cd" />
<public type="id" name="home_button" id="0x7f1200ce" />
<public type="id" name="halo" id="0x7f1200cf" />
Drawable
<public type="drawable" name="halo" id="0x7f020073" />
<public type="drawable" name="ic_sysbar_opa_blue" id="0x7f02012d" />
<public type="drawable" name="ic_sysbar_opa_green" id="0x7f02012e" />
<public type="drawable" name="ic_sysbar_opa_red" id="0x7f02012f" />
<public type="drawable" name="ic_sysbar_opa_yellow" id="0x7f020130" />
Style
<public type="style" name="DotStyle" id="0x7f13022b" />
Write all the lines down in a txt file “SystemUIGoogle.txt”, then, open the \SystemUI\res\value\public.xml, find the corresponding data, you should get the same number of lines, but they carried different id (the 0x7f****** value) , write all the lines down in another txt file “SystemUI.txt”.
At this point, we have got all the new resources’s ids, it is time to bring up the controller in \smali folder.
Step 3.2 Open \SystemUIGoogle\smali_classes2\com\google\android\systemui\ folder, I know that is a little deep… the smali files control the navigation bar animation display or not is among these files, those file is Pixel exclusive, I think it will not going to open sources. Which one is the key? You should have guessed, it’s the OpaLayout.smali, and all those sub-class files start with OpaLayout.
Select these files and copy them to SystemUI\smali\com\google\android\systemui\ folder, you need to create all the folders, then paste those files. File list:
Constants.smali
OpaLayout$-void_setOnLongClickListener_android_view_View$OnLongClickListener_l_LambdaImpl0.smali
OpaLayout$1.smali
OpaLayout$2.smali
OpaLayout$3.smali
OpaLayout$4.smali
OpaLayout$5.smali
OpaLayout$6.smali
OpaLayout.smali
Step 3.3 Ok, You are close to success. Now open the SystemUI\smali\com\google\android\systemui\OpaLayout.smali file with Sublime Text, Ctrl+Shift+F open search find in files box, use keyword “0x7f”, select “SystemUI\smali\com\google\android\systemui\OpaLayout.smali” file, Search result will be like:
Normally you will get 25 matches, double click the match keyword can locate the value, the original value is the id of resources for Pixel, we need to modify them to the new ids for your Rom. Open the txt files you just recorded two groups of ids, put them on the screen side by side, it is easier to operate. Now change all the 25 matches to new value.
For example, the first match, “0x7f1002aa” , we can find “0x7f1002aa” in line
<public type="dimen" name="opa_line_x_collapse_ry" id="0x7f1002aa" />
in SystemUIGoogle.txt, then in the SystemUI.txt we have “0x7f1002ab” with the same resources “opa_line_x_collapse_ry”, <public type="dimen" name="opa_line_x_collapse_ry" id="0x7f1002ab" />
, then we change the first match “0x7f1002aa” to “0x7f1002ab” in sublime (jump to current file by double click the matches), then go back to results check the next one, and so on to modify all of the matches. If they have the same id then you don’t need to modify it. Ps, the new ids will be different with different Roms, you must compile SystemUI once to get the unique ids for your Rom.
Step 3.4 Change the ids will not enough, we need to break the control rule of the Pixel limitations, this is not easy to find, you have to track the whole process of display animation. But I already do it for you, the switch is located in line 2620:
Update 12/08 for Android 7.1.1 Final Version.
The OpaLayout.smali file has been changed in new version, well we need to adjust something, but just a little bit change, the new switch is located in line 2591:
const-string/jumbo v2, "systemui.google.opa_enabled"
invoke-static {}, Lcom/android/keyguard/KeyguardUpdateMonitor;->getCurrentUser()I
move-result v3
invoke-static {v0, v2, v1, v3}, Landroid/provider/Settings$Secure;->getIntForUser(Landroid/content/ContentResolver;Ljava/lang/String;II)I
move-result v0
if-eqz v0, :cond_0
const/4 v0, 0x1
:goto_0
invoke-virtual {p0, v0}, Lcom/google/android/systemui/OpaLayout;->setOpaEnabled(Z)V
The code is reading the global settings of “systemui.google.opa_enabled“, it will be false in all non-Pixel phones even you active the Google Assistant by modify build.prop, so we have to force it to return true to get the Pixel animation working by adding a line
const/4 v0, 0x1
before if-eqz v0, :cond_0
Code will be look like this after you add the line:
const-string/jumbo v2, "systemui.google.opa_enabled"
invoke-static {}, Lcom/android/keyguard/KeyguardUpdateMonitor;->getCurrentUser()I
move-result v3
invoke-static {v0, v2, v1, v3}, Landroid/provider/Settings$Secure;->getIntForUser(Landroid/content/ContentResolver;Ljava/lang/String;II)I
move-result v0
const/4 v0, 0x1
if-eqz v0, :cond_0
const/4 v0, 0x1
:goto_0
invoke-virtual {p0, v0}, Lcom/google/android/systemui/OpaLayout;->setOpaEnabled(Z)V
Step 3.5 Ok, Our mission is about to finish. Last thing is compile SystemUI.apk again.
java -jar apktool.jar b SystemUI -c
if everything goes well, I mean no error appear while compiling files, congratulations, you have succeed in porting Pixel navigation bar animation, use any root file managers to replace the original SystemUI.apk with the new one. (Backup first) And remember to set the file permissions to rw-r-r.