Hi community!! Here is a complete guide showing you how to create an "External Settings App" to control SystemUI.apk look and feel.
I have received tons of pm and mentions from users asking me how did I manage such "magic" work into my mods over Xperia's firmware, so I have decided to create this thread to share you "the concept" I've created. I really hope this guide help people to create your own settings apk from scratch, but not just for the SystemUI.apk, but for anyone in which you need/want to control.
I know that Xposed Framework do the same and also it is the right choice, mine choice too, but the meaning of this thread is: Knowledge exchange!!
Talking about Xposed Framework, here is my module to Xperia devices.
Back to topic...
First of all, a brief feedback about my mods over xda that use concept you will see:
- Serajr Swiping Power Toggles - SystemUI
- Xperia SystemUI + 23 JB Toggles + Sliders + Apps
- Xperia GX Home Launcher for All Xperias (Multi-Resolution)
Let's do it!!
Requirements:
- Java programming skills (I'M NOT GOING TO SHOW HOW TO BUILD APPS OUTSIDE THREAD CONTEXT, NEITHER HOW TO DEVELOP ON THAT PLATFORM)
- Apktool decompile/compile skills (I'M NOT GOING TO SHOW HOW DO DO THAT)
- Logcat (in case you face any FC)
My lab:
- Windows 7 x64
- Eclipse
- Android SDK
- Java
- Apktool
My precious:
- Sony Xperia ZQ
- JB 4.3
External Settings App:
It has a very simple ui and code, with a single CheckBoxPreference!
Don't mind about deprecated functions, they are there purposely to make the simplest possible code. We know that the right choice for preferences is PreferenceFragment.
Java - External Settings App - Eclipse Project:
MainActivity.java
Every field and method contents is commented on for better understanding, but here, what really metters is:
Save current preference value to Settings.System
The app needs to have proper permission to write there, and the right one is:
To make things easier in the future, I've created a "clone" PhoneStatusBar.java class with full package path too, and within it are all related code that "does the magic" (I'll explain every method further). This class has no function over our settings app context (NEVER INSTANTIATE IT), it is there just to allow us to write our SystemUI modifications in Java and get the Smali easily!!!
Take a look:
Here is where SystemUI.apk creates all its contents, so we need to call our setup and handle methods here too.
Here we setup our content observer (preferences keys we created in our settings app).
Here we filter which preference key has changed its state and do proper changes.
Here we set the status bar clock text color according current preference state.
I've also created a inner class that will store our preferences keys Uri's to later pass changed ones to the callback method!
That´s it!!
Now we need to go deep into the "necessary evil" titled: smali !!
1. Get the newly built ExternalPreferences.apk from Eclipse project ( \bin folder )
2. Decompile it
3. Go to ExternalPreferences.apk decompiled folder: ...\smali\com\android\systemui\statusbar\phone\
4. Copy PhoneStatusBar$MyExternalPreferencesObserver.smali file and paste it under SystemUI.apk respective folder, than you will get something like this file structure:
5. While on SystemUI.apk folder, open PhoneStatusBar.smali file (with notepad++)
6. Let's implement our observer class under annotations MemberClasses (in red)
Obs.: JUST last item has no , (comma) after its name!
Now let's copy our three new methods from ExternalPreferences.apk (PhoneStatusBar.smali) file to SystemUI.apk (PhoneStatusBar.smali file)
By copying them you will get (in red):
Under # direct methods
Under # virtual methods
Before going on, we need to fix some stuff within our new methods (just within them) due diffs from our Java "clone" class and real one!!
From (red):
To blue:
Pay attention on mContext field too!!
7. While on SystemUI.apk look for this method:
8. Found, now look for its return-object line (in blue), something like (yours can be tottaly different from mine, but return-object is there!!):
9. Found, now let's implement "calls" to our setup and handle methods (in red)
- First "call" setup our Content Observer preference key list!
- Second "call" ensures that clock text color will be red if preference enabled (just after boot)!
10. Double check your changes, save everything, compile your modified SystemUI.apk and... done!!
11. Don't forget to check out 3th post for new mods in which you can complement your project!!
Known issues:
You tell me!!
To do:
You tell me too!!
Special thanks: (if I forgot someone, please remember me!!)
- Sony
- @poria1999 for the encouragement (I blame you by this guide)
If you like it, press thanks... Simple so!!
.
.
I have received tons of pm and mentions from users asking me how did I manage such "magic" work into my mods over Xperia's firmware, so I have decided to create this thread to share you "the concept" I've created. I really hope this guide help people to create your own settings apk from scratch, but not just for the SystemUI.apk, but for anyone in which you need/want to control.
I know that Xposed Framework do the same and also it is the right choice, mine choice too, but the meaning of this thread is: Knowledge exchange!!
Talking about Xposed Framework, here is my module to Xperia devices.
Back to topic...
First of all, a brief feedback about my mods over xda that use concept you will see:
- Serajr Swiping Power Toggles - SystemUI
- Xperia SystemUI + 23 JB Toggles + Sliders + Apps
- Xperia GX Home Launcher for All Xperias (Multi-Resolution)
Let's do it!!
Requirements:
- Java programming skills (I'M NOT GOING TO SHOW HOW TO BUILD APPS OUTSIDE THREAD CONTEXT, NEITHER HOW TO DEVELOP ON THAT PLATFORM)
- Apktool decompile/compile skills (I'M NOT GOING TO SHOW HOW DO DO THAT)
- Logcat (in case you face any FC)
My lab:
- Windows 7 x64
- Eclipse
- Android SDK
- Java
- Apktool
My precious:
- Sony Xperia ZQ
- JB 4.3
External Settings App:
It has a very simple ui and code, with a single CheckBoxPreference!
Don't mind about deprecated functions, they are there purposely to make the simplest possible code. We know that the right choice for preferences is PreferenceFragment.
Java - External Settings App - Eclipse Project:
MainActivity.java
Every field and method contents is commented on for better understanding, but here, what really metters is:
Save current preference value to Settings.System
The app needs to have proper permission to write there, and the right one is:
Code:
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
To make things easier in the future, I've created a "clone" PhoneStatusBar.java class with full package path too, and within it are all related code that "does the magic" (I'll explain every method further). This class has no function over our settings app context (NEVER INSTANTIATE IT), it is there just to allow us to write our SystemUI modifications in Java and get the Smali easily!!!
Take a look:
PHP:
makeStatusBarView()
PHP:
setupExternalSettingsObserver()
PHP:
externalPreferencesObserverCallback(Uri uri)
PHP:
handleStatusBarChangeClockToRed()
PHP:
private class MyExternalPreferencesObserver extends ContentObserver
That´s it!!
Now we need to go deep into the "necessary evil" titled: smali !!
1. Get the newly built ExternalPreferences.apk from Eclipse project ( \bin folder )
2. Decompile it
3. Go to ExternalPreferences.apk decompiled folder: ...\smali\com\android\systemui\statusbar\phone\
4. Copy PhoneStatusBar$MyExternalPreferencesObserver.smali file and paste it under SystemUI.apk respective folder, than you will get something like this file structure:
Code:
PhoneStatusBar$FastColorDrawable.smali
PhoneStatusBar$H.smali
[COLOR="Red"]PhoneStatusBar$MyExternalPreferencesObserver.smali[/COLOR]
PhoneStatusBar$MyTicker.smali
PhoneStatusBar.smali
5. While on SystemUI.apk folder, open PhoneStatusBar.smali file (with notepad++)
6. Let's implement our observer class under annotations MemberClasses (in red)
Code:
# annotations
.annotation system Ldalvik/annotation/MemberClasses;
value = {
Lcom/android/systemui/statusbar/phone/PhoneStatusBar$FastColorDrawable;[COLOR="Red"],[/COLOR]
Lcom/android/systemui/statusbar/phone/PhoneStatusBar$MyTicker;[COLOR="red"],[/COLOR]
Lcom/android/systemui/statusbar/phone/PhoneStatusBar$H;[COLOR="red"],[/COLOR]
[COLOR="red"]Lcom/android/systemui/statusbar/phone/PhoneStatusBar$MyExternalPreferencesObserver;[/COLOR]
}
.end annotation
Now let's copy our three new methods from ExternalPreferences.apk (PhoneStatusBar.smali) file to SystemUI.apk (PhoneStatusBar.smali file)
By copying them you will get (in red):
Under # direct methods
Code:
# direct methods
[COLOR="Red"].method private handleStatusBarChangeClockToRed()V
.locals 7
.prologue
.line 127
iget-object v3, p0, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->mContext:Landroid/content/Context;
invoke-virtual {v3}, Landroid/content/Context;->getResources()Landroid/content/res/Resources;
move-result-object v3
const-string v4, "clock"
const-string v5, "id"
iget-object v6, p0, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->mContext:Landroid/content/Context;
invoke-virtual {v6}, Landroid/content/Context;->getPackageName()Ljava/lang/String;
move-result-object v6
invoke-virtual {v3, v4, v5, v6}, Landroid/content/res/Resources;->getIdentifier(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I
move-result v1
.line 128
.local v1, resId:I
iget-object v3, p0, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->mStatusBarView:Landroid/view/View;
invoke-virtual {v3, v1}, Landroid/view/View;->findViewById(I)Landroid/view/View;
move-result-object v0
check-cast v0, Landroid/widget/TextView;
.line 131
.local v0, clock:Landroid/widget/TextView;
iget-object v3, p0, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->mContext:Landroid/content/Context;
invoke-virtual {v3}, Landroid/content/Context;->getResources()Landroid/content/res/Resources;
move-result-object v3
const-string v4, "TextAppearance.StatusBar.Clock"
const-string v5, "style"
iget-object v6, p0, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->mContext:Landroid/content/Context;
invoke-virtual {v6}, Landroid/content/Context;->getPackageName()Ljava/lang/String;
move-result-object v6
invoke-virtual {v3, v4, v5, v6}, Landroid/content/res/Resources;->getIdentifier(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I
move-result v1
.line 132
iget-object v3, p0, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->mContext:Landroid/content/Context;
invoke-virtual {v0, v3, v1}, Landroid/widget/TextView;->setTextAppearance(Landroid/content/Context;I)V
.line 136
iget-object v3, p0, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->mContext:Landroid/content/Context;
invoke-virtual {v3}, Landroid/content/Context;->getContentResolver()Landroid/content/ContentResolver;
move-result-object v3
const-string v4, "status_bar_change_clock_to_red"
const/4 v5, 0x0
invoke-static {v3, v4, v5}, Landroid/provider/Settings$System;->getInt(Landroid/content/ContentResolver;Ljava/lang/String;I)I
move-result v2
.line 137
.local v2, value:I
const/4 v3, 0x1
if-ne v2, v3, :cond_0
.line 140
const/high16 v3, -0x1
invoke-virtual {v0, v3}, Landroid/widget/TextView;->setTextColor(I)V
.line 143
:cond_0
return-void
.end method
.method private setupExternalSettingsObserver()V
.locals 5
.prologue
.line 44
new-instance v1, Ljava/util/LinkedList;
invoke-direct {v1}, Ljava/util/LinkedList;-><init>()V
.line 47
.local v1, list:Ljava/util/List;,"Ljava/util/List<Lcom/android/systemui/statusbar/phone/PhoneStatusBar$MyExternalPreferencesObserver;>;"
new-instance v2, Lcom/android/systemui/statusbar/phone/PhoneStatusBar$MyExternalPreferencesObserver;
iget-object v3, p0, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->mHandler:Landroid/os/Handler;
const-string v4, "status_bar_change_clock_to_red"
invoke-static {v4}, Landroid/provider/Settings$System;->getUriFor(Ljava/lang/String;)Landroid/net/Uri;
move-result-object v4
invoke-direct {v2, p0, v3, v4}, Lcom/android/systemui/statusbar/phone/PhoneStatusBar$MyExternalPreferencesObserver;-><init>(Lcom/android/systemui/statusbar/phone/PhoneStatusBar;Landroid/os/Handler;Landroid/net/Uri;)V
invoke-interface {v1, v2}, Ljava/util/List;->add(Ljava/lang/Object;)Z
.line 60
invoke-interface {v1}, Ljava/util/List;->iterator()Ljava/util/Iterator;
move-result-object v0
.line 61
.local v0, iterator:Ljava/util/Iterator;
:goto_0
invoke-interface {v0}, Ljava/util/Iterator;->hasNext()Z
move-result v2
if-nez v2, :cond_0
.line 67
return-void
.line 64
:cond_0
invoke-interface {v0}, Ljava/util/Iterator;->next()Ljava/lang/Object;
move-result-object v2
check-cast v2, Lcom/android/systemui/statusbar/phone/PhoneStatusBar$MyExternalPreferencesObserver;
invoke-virtual {v2}, Lcom/android/systemui/statusbar/phone/PhoneStatusBar$MyExternalPreferencesObserver;->observe()V
goto :goto_0
.end method[/COLOR]
.method public constructor <init>()V
.locals 5
.prologue
const/4 v4, 0x2
const/4 v3, -0x1
const/4 v0, 0x0
const/4 v2, 0x0
.line 140
invoke-direct {p0}, Lcom/android/systemui/statusbar/BaseStatusBar;-><init>()V
.
.
.
Under # virtual methods
Code:
# virtual methods
[COLOR="Red"].method public externalPreferencesObserverCallback(Landroid/net/Uri;)V
.locals 1
.parameter "uri"
.prologue
.line 76
const-string v0, "status_bar_change_clock_to_red"
invoke-static {v0}, Landroid/provider/Settings$System;->getUriFor(Ljava/lang/String;)Landroid/net/Uri;
move-result-object v0
invoke-virtual {p1, v0}, Landroid/net/Uri;->equals(Ljava/lang/Object;)Z
move-result v0
if-eqz v0, :cond_0
.line 79
invoke-direct {p0}, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->handleStatusBarChangeClockToRed()V
.line 115
:cond_0
return-void
.end method[/COLOR]
.method public addIcon(Ljava/lang/String;IILcom/android/internal/statusbar/StatusBarIcon;)V
.locals 5
.parameter "slot"
.parameter "index"
.parameter "viewIndex"
.parameter "icon"
.prologue
.line 1323
new-instance v0, Lcom/android/systemui/statusbar/StatusBarIconView;
iget-object v1, p0, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->mContext:Landroid/content/Context;
const/4 v2, 0x0
invoke-direct {v0, v1, p1, v2}, Lcom/android/systemui/statusbar/StatusBarIconView;-><init>(Landroid/content/Context;Ljava/lang/String;Landroid/app/Notification;)V
.
.
.
Before going on, we need to fix some stuff within our new methods (just within them) due diffs from our Java "clone" class and real one!!
From (red):
Code:
iget-object v3, p0, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->mStatusBarView:[COLOR="red"]Landroid/view/View;[/COLOR]
invoke-virtual {v3, v1}, [COLOR="red"]Landroid/view/View;[/COLOR]->findViewById(I)Landroid/view/View;
To blue:
Code:
iget-object v3, p0, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->mStatusBarView:[COLOR="blue"]Lcom/android/systemui/statusbar/phone/PhoneStatusBarView;[/COLOR]
invoke-virtual {v3, v1}, [COLOR="blue"]Lcom/android/systemui/statusbar/phone/PhoneStatusBarView;[/COLOR]->findViewById(I)Landroid/view/View;
Pay attention on mContext field too!!
7. While on SystemUI.apk look for this method:
Code:
.method protected makeStatusBarView()Lcom/android/systemui/statusbar/phone/PhoneStatusBarView;
8. Found, now look for its return-object line (in blue), something like (yours can be tottaly different from mine, but return-object is there!!):
Code:
.line 1005
const-string v0, "com.sonymobile.notes.NEW_SKETCH"
invoke-direct {p0, v0}, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->isAppInstalled(Ljava/lang/String;)Z
move-result v0
iput-boolean v0, p0, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->mRightVisible:Z
.line 1007
iget-object v0, p0, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->mStatusBarView:Lcom/android/systemui/statusbar/phone/PhoneStatusBarView;
[B][COLOR="Blue"]return-object[/COLOR][/B] v0
:cond_14
move v1, v2
.line 825
goto/16 :goto_7
9. Found, now let's implement "calls" to our setup and handle methods (in red)
Code:
.line 1005
const-string v0, "com.sonymobile.notes.NEW_SKETCH"
invoke-direct {p0, v0}, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->isAppInstalled(Ljava/lang/String;)Z
move-result v0
iput-boolean v0, p0, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->mRightVisible:Z
[COLOR="Red"].line 34
invoke-direct {p0}, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->setupExternalSettingsObserver()V
.line 37
invoke-direct {p0}, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->handleStatusBarChangeClockToRed()V[/COLOR]
.line 1007
iget-object v0, p0, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->mStatusBarView:Lcom/android/systemui/statusbar/phone/PhoneStatusBarView;
[B][COLOR="Blue"]return-object[/COLOR][/B] v0
:cond_14
move v1, v2
.line 825
goto/16 :goto_7
- Second "call" ensures that clock text color will be red if preference enabled (just after boot)!
10. Double check your changes, save everything, compile your modified SystemUI.apk and... done!!
11. Don't forget to check out 3th post for new mods in which you can complement your project!!
Known issues:
You tell me!!
To do:
You tell me too!!
Special thanks: (if I forgot someone, please remember me!!)
- Sony
- @poria1999 for the encouragement (I blame you by this guide)
If you like it, press thanks... Simple so!!
.
.
Attachments
Last edited: