Some of you might be interested in replacing the android provided status bar with their own creative stuffs on top of the phone, instead the standard one provided by android.In this post i am going to explain how you can replace existing android's standard status bar view with your view.
Layout for android status bar is defined in
/frameworks/base/core/res/res/layout/status_bar.xmlThe inflation part is handled in StatusBarService.java
/frameorks/base/services/java/com/android/server/status/StatusBarService.javaLine 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 questionInclude a entry in
base/android.mkThis 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 questionWhen 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/frameworkYou need to push this apk also.
In order for the 'mystatus' folder to be compiled you need to compile the base.
mmm frameworks/baseout/target/product/generic/system/framework/framework.jarframework.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/javaout/target/product/generic/system/framework/services.jarservices.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/frameworkFor safer side, push the entire framework contents to your device.
adb -d push out/target/product/generic/system/framework/ /system/frameworkSo 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