[Android Software Development] serajr:
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:

<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:

makeStatusBarView()

Here is where SystemUI.apk creates all its contents, so we need to call our setup and handle methods here too.

setupExternalSettingsObserver()

Here we setup our content observer (preferences keys we created in our settings app).

externalPreferencesObserverCallback(Uri uri)

Here we filter which preference key has changed its state and do proper changes.

handleStatusBarChangeClockToRed()

Here we set the status bar clock text color according current preference state.

private class MyExternalPreferencesObserver extends ContentObserver

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:

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)

# 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

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

# 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

# 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):

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:

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:

.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!!):

.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)

.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

- 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

- [MENTION=5146111]poria1999[/MENTION] for the encouragement (I blame you by this guide)

If you like it, press thanks... Simple so!! ;)

.

.