Layout for android status bar is defined in
/frameworks/base/core/res/res/layout/status_bar.xml
The inflation part is handled in StatusBarService.java
/frameorks/base/services/java/com/android/server/status/StatusBarService.java
Line no 256 ( Roughly )
private void makeStatusBarView(Context context) {
Resources res = context.getResources();
mRightIconSlots = res.getStringArray(com.android.internal.R.array.status_bar_icon_order);
mRightIcons = new StatusBarIcon[mRightIconSlots.length];
ExpandedView expanded = (ExpandedView)View.inflate(context,
com.android.internal.R.layout.status_bar_expanded, null);
expanded.mService = this;
StatusBarView sb = (StatusBarView)View.inflate(context,
com.android.internal.R.layout.status_bar, null);
...............................
...............................
...............................
}
The place where the status bar view added is
public void systemReady() {
final StatusBarView view = mStatusBarView;
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
view.getContext().getResources().getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height),
WindowManager.LayoutParams.TYPE_STATUS_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING,
mPixelFormat);
lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
lp.setTitle("StatusBar");
lp.windowAnimations = R.style.Animation_StatusBar;
WindowManagerImpl.getDefault().addView(view, lp);
}
The status bar view which was inflated earlier is added to WindowManager.
WindowManagerImpl.getDefault().addView(view, lp);
In order for your view to be shown, simple, you need to bring in your 'View' instance and add it here at this point. DONE...!!
So simple isn't it...!!
But, well where will you keep your view handling/creating (i mean the .java file), how will you show default status bar notifications( like antenna, wifi, bluetooth ) every thing which a status bar shows now.
1 - Create a folder in /frameworks/base named 'mystatus'
2 - Create java and res folders and create folders inside 'java' as per package names and keep the resources, layouts files you need under res corresponding folders ( drawable-hdpi , layouts )
java->com->mani->mystatus
res->drawable-hdpi
res->drawable-mdpi
res->layouts
res->assets
res->anim ( if any required )
3 - In this way you have all your independent view creation/public exposed apis present in a seperate folder in framework.
MyStatusBarView.java
package com.mani.mystatus;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.TextView;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.graphics.Typeface;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.LevelListDrawable;
import android.graphics.drawable.AnimationDrawable;
public final class MyStatusBarView extends LinearLayout{
private TextView batterytext;
private TextView batterylevel;
private TextView batterypercentage;
public MyStatusBarView(Context context, AttributeSet attrs){
super(context, attrs);
}
public MyStatusBarView(Context context){
super(context);
batterytext = new TextView(context);
batterytext.setTextSize(20);
batterytext.setPadding(7,0,0,0);
batterytext.setTextColor(Color.GRAY);
batterytext.setText("MANI - My status bar");
batterylevel = new TextView(context);
batterylevel.setTextSize(22);
batterylevel.setPadding(7,0,0,0);
batterylevel.setTextColor(Color.RED);
batterypercentage = new TextView(context);
batterypercentage.setTextSize(18);
batterypercentage.setPadding(2,0,0,0);
batterypercentage.setText("%");
batterypercentage.setTextColor(Color.RED);
LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.FILL_PARENT);
LinearLayout.LayoutParams levelParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.FILL_PARENT);
LinearLayout.LayoutParams percentageParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.FILL_PARENT);
batterytext.setLayoutParams(textParams);
batterylevel.setLayoutParams(levelParams);
batterypercentage.setLayoutParams(percentageParams);
this.setBackgroundColor(Color.BLUE);
this.addView(batterytext);
this.addView(batterylevel);
this.addView(batterypercentage);
}
public void setBatteryLevel(int level)
{
String blevel = "";
blevel+= level;
batterylevel.setText(blevel);
}
}
Changes required in StatusBarService.java
------------------------------------------
These are the four steps you need to implement.
1- Import the view class
import com.mani.mystatus.MyStatusBarView;
2 - Declare a global variable to MyStatusBarView
MyStatusBarView myStatusBarView;
3 - Create a instance (NOTE: We are passing context obtained in StatusBarService)
myStatusBarView = new MyStatusBarView(mContext);
4 - Add the view instance to WindowManager
WindowManagerImpl.getDefault().addView(myStatusBarView, lp);
I am going to show you how to display battery percentage in the custom view. (later you can implement what are all the details you require from statusbarservice and send it back to your view using public exposed apis).
So i am going to expose a public function in MyStatusBarView class to get the battery percent and display it.
public void setBatteryLevel(int level);
In StatusBarService.java:
public void updateBatteryStats(IconData batteryData)
{
myStatusBarView.setBatteryLevel(batteryData.iconLevel);
}
In order for the framework compilation to pick up java files from 'mystatus' folder, add a entry in the file
/build/core/pathmap.mk as below.
1 - pathmap.mk /build --> Add the folder.
FRAMEWORKS_BASE_SUBDIRS := \
$(addsuffix /java, \
core \
mystatus \
graphics \
location \
media \
opengl \
sax \
telephony \
wifi \
vpn \
keystore \
)
Resources usuage:
We might be interested in using resources ( like images, layouts) in status bar view. Include your resources in res folder. Now i have two questions for you
1 - We need to have R.java file for compilation of java files in 'mystatus' (if in case if uses import com.mani.mystatus.R ).How we import these ?
2 - We need these resources to be brought to the devices. How we do this ?
For the first question
Include a entry in base/android.mk
This where framework-res R files are included before compilation.
(roughly line no 194 )
LOCAL_INTERMEDIATE_SOURCES := \
$(framework-res-source-path)/android/R.java \
$(framework-res-source-path)/android/Manifest.java \
$(framework-res-source-path)/com/android/internal/R.java \
So we need to include our newly created R.java file to this path.
mystatus-source-path := APPS/mystatus-res_intermediates/src
LOCAL_INTERMEDIATE_SOURCES := \
$(framework-res-source-path)/android/R.java \
$(framework-res-source-path)/android/Manifest.java \
$(framework-res-source-path)/com/android/internal/R.java \
$(mystatus-source-path)/com/mani/mystatus/R.java
For second question
When R.java files will be created ??. We need to compile the resources directory.
A resources directory will be compiled only if the compilation is for android application. ( i.e we need to create a apk out of it to see the R.java file generated)
How do we make 'mystatus' a application. ( No activity is present but that is okay )
Keep a AndroidManifest.xml under a res folder and a 'android.mk' file.
AndroidManifest.xml:
This manifest file basically tells the Resources will be under the package name 'com.mani.mystatus'
Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_STATIC_JAVA_LIBRARIES := \
android-common
LOCAL_PACKAGE_NAME := mystatus-res
# Install this alongside the libraries.
LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES)
# Create package-export.apk, which other packages can use to get
# PRODUCT-agnostic resource data like IDs and type definitions.
LOCAL_EXPORT_PACKAGE_RESOURCES := true
include $(BUILD_PACKAGE)
Android.mk file is required for you to compile the subsystem from root directory.
For more understanding of android build process, what is going on when apk is built, please check below link.
http://www.alittlemadness.com/2010/06/07/understanding-the-android-build-process/
# make mystatus-res (or) # make framework/base/mystatus/res/
'mystatus-res.apk' will be created and kept in
out/target/product/generic/system/framework
You need to push this apk also.
In order for the 'mystatus' folder to be compiled you need to compile the base.
mmm frameworks/base
out/target/product/generic/system/framework/framework.jar
framework.jar contains the dex files of 'mystatus' java files.
In order to compile the status bar service changes, compile the services part.
mmm frameworks/base/services/java
out/target/product/generic/system/framework/services.jar
services.jar will have the changes/ recent modified changes in statusbarservice.java.
So totally three files files needs to be pused the device.
adb -d remount
adb -d push out/target/product/generic/system/framework/services.jar /system/framework
adb -d push out/target/product/generic/system/framework/framework.jar /system/framework
adb -d push out/target/product/generic/system/framework/mystatus-res.apk /system/framework
For safer side, push the entire framework contents to your device.
adb -d push out/target/product/generic/system/framework/ /system/framework
So now when the system boots up, you would be seeing your own status bar view (MyStatusBarView).
Enjoy the work...!!
Snapshots for your reference:
This was tried out on vanilla android 2.2
Hi Mani,
ReplyDeleteI just tried out what you have posted, it works fine. I tried adding a icon on the fly on mystatusbarview.java, placed the icon image in drawable of the mystatus/res/drawable , this compiles fine. When I try it on my device , it throws the below error
'android.content.res.Resources$NotFoundException: Resource ID #0x7f020001
E/AndroidRuntime( 1370): at android.content.res.Resources.getValue(Resources.java:891)
E/AndroidRuntime( 1370): at android.content.res.Resources.getDrawable(Resources.java:579)
E/AndroidRuntime( 1370): at android.view.View.setBackgroundResource(View.java:7186)
E/AndroidRuntime( 1370): at com.mist.customstatus.CustomStatusBarView.setVolumeLealarm_release: clear alarm, pending 0
'
In framework/base/Android.mk , I have added path of the this new status bar R.java file.
Do you have any idea what could be going wrong?
REgards
Krt
Hi...Kitty...!!
ReplyDeleteThe problem could be u might not have used the right 'context' instance to pick up the Resources.
What i mean is,, you need to create a 'context' from the package where mystatus folder is present and use that context to inflate the view from mystatus-res.apk
MystatusContext = context.createPackageContext("com.frameworks.mystatus", Context.CONTEXT_IGNORE_SECURITY);
in mystatus-res.apk --> AndroidManifest.xml
check what is the package name you have specified. Please use that package name and get a context and access the resources from this context. it should work fine.
Any issues, pls revert back to me ..!!
Thanks,
mani
Hi Mani,
ReplyDeleteI have not tried modifying Android source yet, can you please tell me, steps to build the modified source.
Also tell me, whether we can test the modified source with emulator? if so, how to do it?
Thanks,
Swathi
Swathi..
ReplyDeleteYou can launch the emulator from AOSP (Source code) which can reflect the changes made ins status bar.
For compiling source code.
1 # . ./build/envsetup.sh
2 # lunch 1
3 # make -j4
Once the compilation is successfull (without any changes made) start making the changes as mentioned above and do "make -j4" again.
Once it is successfull, go to
#out/target/product/generic/system
You would see three img files created. system.img,boot.img,recover.img.
Then launch the emulator as like this.
#emulator -skin 1200x500.
Now you would see the emualtor having the status bar changes.
Thanks,
Mani
Thanks,
Mani
Hello,
ReplyDeleteYour method is very usefull. I have create a complete moded status bar and he work fine on emulator.
Now I want put it on galaxy tab! I have succed for services.odex and android.policy.odex with this method : http://www.drakaz.com/forum/viewtopic.php?pid=1002#p1002
Now I try to push mystatus-res.apk in tablet, but this create lots of errors about framework ressources. I think that it's signing problem, do you have an idea? Do you think that I could edit framework-res.apk?
Thanks
This is a beautiful post.
ReplyDeleteIt covers actually many concepts.
Thnx :)
@Anonymous:
ReplyDeleteThe method i was explaining will work on emulator or on the device which u are manufacturing and u have full access.. i mean root access..!!
Coz u need to push the mystatus-res.apk into /system/app, which basically u cannot do in the other devices like Galaxy tab. Plus u need to replace the "services.jar" in the system. So this would be useful if you are having your own device and code base for it. (BSP).
thanks,
Mani
Thanks it's work. I have my custom status bar on rooted galaxy tab.
ReplyDeleteregards
Hi there,
ReplyDeletegreat post, thanks for it.
I have little problem, i'm trying to achieve similar thing but in frameworks/opt. I've created there dir with my package name and done everything analogous.
During compilation i got following error:
error: cannot read: out/target/common/obj/APPS/xxx-xxx-res_intermediates/src/com/xxx/xxx/xxx/Manifest.java
(changed to xxx because these're my client's private data)
Do you maybe know what can cause it?
Thank you
LOCAL_INTERMEDIATE_SOURCES := \
ReplyDelete$(framework-res-source-path)/android/R.java \
$(framework-res-source-path)/android/Manifest.java \
You can remove this line,if you have this problem. I can help more if u can share the Android.mk file.
hi,
ReplyDeletehow to implement back button in status bar?
hi,
ReplyDeletewhile building android source code i am getting this error:out/target/common/obj/APPS/SystemUI_intermediates/src/com/android/systemui/R.java:10: duplicate class: com.android.systemui.R
Even if I execute "rm" command to remove that file or execute "make clean".why is it so?
I used the same command to build that you mentioned earlier.
I am trying to add a new layout to the framework say my_layout.
ReplyDeleteI succeeded partially in that .
I defined my layout in an xml and put it inside frameworks/base/core/res/res/layout
I then mentioned the id in frameworks/base/core/res/res/values public.xml and ids.xml
And after performing a complete build :
make framework
make update-api
make
i am able to access it as android.R.layout.my_layout
But now i want to access it through xml also :Eg: "@android:layout/my_layout"
In which place in the source do i mention this reference ?
I resolved it ! I put the files into the sdk also !
Deletehey Maniii.
ReplyDeletei write code in my app
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, 30,
WindowManager.LayoutParams.TYPE_STATUS_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
PixelFormat.TRANSLUCENT);
lp.gravity = Gravity.CLIP_HORIZONTAL | Gravity.TOP;
WindowManager wm = (WindowManager) getApplicationContext()
.getSystemService(WINDOW_SERVICE);
wm.addView(view, lp);
but it's give me an error
"Caused by: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@416607a0 -- permission denied for this window type"
and also having issue with import "com.android.internal.R."
can you please help me i want to add one view in status bar but not notification
thanks in advance :)
Is it passible to do this in an APP on a non-rooted device? Thanks.
ReplyDeleteIn non-rooted device you cannot do this...!!
ReplyDeleteAwesome.. Thanks for the directory structure explanation of the framework/base/*/com/*/ and it's registration in pathmap.mk
ReplyDeleteThanks....!!!
ReplyDeleteHi I want to change position of system bar from bottom to top. can you please help on that.
ReplyDeleteCould you send me that example to my mail id rajeshmcashc10@gmail.com
ReplyDeleteCould you explain how does the volume buttons are handled by the system? Where do I have to change if I have to change its UI (i.e. UI shown when there is a volume change)
ReplyDeleteIt seems possible to replace status bar on non-rooted devices. I don't know how but there are some apps on the market which add a specific status bar : https://play.google.com/store/apps/details?id=com.tombarrasso.android.wp7barfree
ReplyDeleteAny idea ?
Im looking for some piece of guide as what you re looking for.
DeleteNot found yet :(
hi mani is ur statusbar replaces android default status bar without rooting the device.
ReplyDeletehi mani,
ReplyDeleteIs their any way to control hide and visible of status bar when we are ohter application like setting screen. Is thier any way to do that. Thanks
Hi mani,
ReplyDeleteI tried your method but its not working.it only gives me following error when i try to compile using command make make framework/base/mystatus/res/ .
Please help...
find: `out/target/common/docs/gen': No such file or directory
build/core/java.mk:9: *** frameworks/base/mystatus/res: Target java module does not define any source or resource files. Stop.
Hi Mani,
ReplyDeleteI am trying to modify the Statusbar and Navigation Bar height by modifying
frameworks/base/core/res/res/values/dimens.xml in Android 6.0 AOSP code. I have a rooted device as well. I build and replaced the service.jar , framework.jar and ext.jar in the system/framework folder of my device. But the height is not changing. I replaced the SystemUI.apk as well. Any help is appreciated.
name="status_bar_height" as 84dp
name="navigation_bar_height" as 148dp
Regards,
Subrat