在上一篇 Android 4.0 平板全屏实现(一) 中,实现了android 4.0 上平板的导航栏的“不可见”,即所有的导航键都不显示,但导航栏所占的区域大小还在,只是显示成一个黑条,这显然不是我们想要的效果。本着打破沙锅问到底的精神,笔者继续翻源码,查阅相关资料,发现4.0 以后的导航栏都是被 SystemUI.apk 的应用控制的,它的源码所在的位置:
frameworks/base/packages/SystemUI
在SystemUI 启动流程 一文中对 SystemUI 有详细的介绍,在这里就不再赘述,各位看官可移步了解之。废话不多说,开始回到正题。我们最终想要得到的效果,就是完全隐藏平板上的导航栏,没有占用任何显示空间。
SystemUI 的启动过程(平板部分)
在Android 4.0 ICS SystemUI浅析——SystemUI启动流程这篇文章中做了详细的介绍,挑几个重要的步骤来讲:
1.
[mw_shl_code=java,true]static final void startSystemUi(Context context) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.systemui",
"com.android.systemui.SystemUIService"));
Slog.d(TAG, "Starting service: " + intent);
context.startService(intent);
} [/mw_shl_code]
2. SystemUIService 启动两个继承自 SystemUI 的 "service", 其中 导航栏的类是根据屏幕分辨率来判断的
[mw_shl_code=java,true] // Pick status bar or system bar.
IWindowManager wm = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
try {
Log.d(TAG, "*** canStatusBarHide = " + wm.canStatusBarHide() + " ***");
SERVICES[0] = wm.canStatusBarHide()
? R.string.config_statusBarComponent
: R.string.config_systemBarComponent;
} catch (RemoteException e) {
Slog.w(TAG, "Failing checking whether status bar can hide", e);
}
final int N = SERVICES.length;
mServices = new SystemUI[N];
for (int i=0; i<N; i++) {
Class cl = chooseClass(SERVICES );
Slog.d(TAG, "loading: " + cl);
try {
mServices = (SystemUI)cl.newInstance();
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InstantiationException ex) {
throw new RuntimeException(ex);
}
mServices.mContext = this;
Slog.d(TAG, "running: " + mServices);
mServices.start();
}
[/mw_shl_code]
如果是平板(wm.canStatusBarHide() = false)则启用 com.android.systemui.statusbar.tablet.TabletStatusBar,这也是我们接下来研究的重点。因为手机已经可以做到完全全屏。
继续跟踪,发现 PhoneStatusBar 和 TabletStatusBar 都继承自 StatusBar, 在 StatusBar 中有一个非常重要的抽象函数 makeStatusBarView(),没错,看名字就可以知道,就是它初始化了SystemUI的主要部分(手机的是 NavigationBar 和 StatusBar, 平板的是 Combination bar). 不管是手机还是平板,都是通过这个函数来生成自己的导航栏,被 StatusBar 调用,具体代码如下:
[mw_shl_code=java,true]public void start() {
// First set up our views and stuff.
View sb = makeStatusBarView();
// Connect in to the status bar manager service
StatusBarIconList iconList = new StatusBarIconList();
ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
mCommandQueue = new CommandQueue(this, iconList);
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
int[] switches = new int[7];
ArrayList<IBinder> binders = new ArrayList<IBinder>();
try {
mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,
switches, binders);
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
disable(switches[0]);
setSystemUiVisibility(switches[1]);
topAppWindowChanged(switches[2] != 0);
// StatusBarManagerService has a back up of IME token and it's restored here.
setImeWindowStatus(binders.get(0), switches[3], switches[4]);
setHardKeyboardStatus(switches[5] != 0, switches[6] != 0);
// Set up the initial icon state
int N = iconList.size();
int viewIndex = 0;
for (int i=0; i<N; i++) {
StatusBarIcon icon = iconList.getIcon(i);
if (icon != null) {
addIcon(iconList.getSlot(i), i, viewIndex, icon);
viewIndex++;
}
}
// Set up the initial notification state
N = notificationKeys.size();
if (N == notifications.size()) {
for (int i=0; i<N; i++) {
addNotification(notificationKeys.get(i), notifications.get(i));
}
} else {
Log.wtf(TAG, "Notification list length mismatch: keys=" + N
+ " notifications=" + notifications.size());
}
// Put up the view
final int height = getStatusBarHeight();
mStatusBarHeight = height;
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
height,
WindowManager.LayoutParams.TYPE_STATUS_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.OPAQUE);
// the status bar should be in an overlay if possible
final Display defaultDisplay
= ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
if (ActivityManager.isHighEndGfx(defaultDisplay)) {
lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
lp.gravity = getStatusBarGravity();
lp.setTitle("StatusBar");
lp.packageName = mContext.getPackageName();
lp.windowAnimations = R.style.Animation_StatusBar;
mStatusBarLayoutParams = lp;
mStatusBarView = sb;
WindowManagerImpl.getDefault().addView(sb, lp);
if (SPEW) {
Slog.d(TAG, "Added status bar view: gravity=0x" + Integer.toHexString(lp.gravity)
+ " icons=" + iconList.size()
+ " disabled=0x" + Integer.toHexString(switches[0])
+ " lights=" + switches[1]
+ " menu=" + switches[2]
+ " imeButton=" + switches[3]
);
}
mDoNotDisturb = new DoNotDisturb(mContext);
}
[/mw_shl_code]
通过上面这段代码,可以看到,在调用makeStatusBarView生成View sb 之后,通过 WindowManagerImpl.getDefault().addView(sb, lp); 将其显示到屏幕之上。追踪到这里,我想大多数同学应该知道怎么去做导航栏的隐藏了。。
没错,很简单,调用 WindowManagerImpl.getDefault().updateView(sb, lp) 就可以搞定。如果是要导航栏始终不可见,将其高度设置为0就可以,但是我们还要将其还原。还得update回来。。
接下来的问题,就是要找个合适的位子,添加上我们的这段代码,在什么地方调用updateView才是最合适的呢?
全屏实现(4.0平板)
由于 StatusBar 是被 SystemUIService 调用的,而且仅仅执行 start() 函数,而 StatusBar 是个普通的Java类,没有Context上下文环境,不具备接收事件的能力(这里可能描述得不够清楚,主要就是说应用层发送的广播啊,startService之类的方法都不被StatusBar接收到。因为我们需要在应用层调用相关cmd来控制导航栏的显示/隐藏)。
我们会发现,SystemUIService 是个普通的 service 能够使用到组件之间的通信之中,并且StatusBar也是在这里被调起来的。开始动工吧,能想到的最简单的调用顺序就是 :
1. 扩展 SystemUI, 添加多一个抽象函数 onReceive(String action)
2. 在 SystemUIService 中注册一个 BroadcastReceiver 监听我们定制的两个 action (隐藏/显示)
3. 在 BroadcastReceive 函数中,调用 SystemUI 的 onReceive函数
4. 在 StatusBar 的 onReceive 函数中处理导航栏的显示/隐藏。
在 SystemUI 中添加抽象函数(frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUI.java)
[mw_shl_code=java,true]public abstract class SystemUI {
public Context mContext;
public abstract void start();
public abstract void onReceive(String action);
protected void onConfigurationChanged(Configuration newConfig) {
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
}
}[/mw_shl_code]
在 SystemUIService 中注册 BroadcastReceiver (frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java)
[mw_shl_code=java,true]@Override
public void onCreate() {
// Pick status bar or system bar.
IWindowManager wm = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
try {
Log.d(TAG, "*** canStatusBarHide = " + wm.canStatusBarHide() + " ***");
SERVICES[0] = wm.canStatusBarHide()
? R.string.config_statusBarComponent
: R.string.config_systemBarComponent;
} catch (RemoteException e) {
Slog.w(TAG, "Failing checking whether status bar can hide", e);
}
final int N = SERVICES.length;
mServices = new SystemUI[N];
for (int i=0; i<N; i++) {
Class cl = chooseClass(SERVICES);
Slog.d(TAG, "loading: " + cl);
try {
mServices = (SystemUI)cl.newInstance();
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InstantiationException ex) {
throw new RuntimeException(ex);
}
mServices.mContext = this;
Slog.d(TAG, "running: " + mServices);
mServices.start();
}
// register broadcast receiver
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_DISPLAY_STATUS_BAR);
intentFilter.addAction(ACTION_HIDE_STATUS_BAR);
registerReceiver(mStatusBarReceiver, intentFilter);
}[/mw_shl_code]
在 onReceive 中分发事件(frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java)
[mw_shl_code=java,true]BroadcastReceiver mStatusBarReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null && !TextUtils.isEmpty(intent.getAction())) {
String action = intent.getAction();
final int N = SERVICES.length;
for (int i = 0; i < N; i++) {
Slog.d(TAG, "invoke: " + mServices + "'s onReceive()");
mServices.onReceive(action);
}
}
}
};[/mw_shl_code]
在StatusBar中处理消息(frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java)
[mw_shl_code=java,true]public abstract class StatusBar extends SystemUI implements CommandQueue.Callbacks {
static final String TAG = "StatusBar";
private static final boolean SPEW = false;
protected CommandQueue mCommandQueue;
protected IStatusBarService mBarService;
// Up-call methods
protected abstract View makeStatusBarView();
protected abstract int getStatusBarGravity();
public abstract int getStatusBarHeight();
public abstract void animateCollapse();
private DoNotDisturb mDoNotDisturb;
private View mStatusBarView;
private int mStatusBarHeight;
private WindowManager.LayoutParams mStatusBarLayoutParams;
public void start() {
// First set up our views and stuff.
View sb = makeStatusBarView();
// Connect in to the status bar manager service
StatusBarIconList iconList = new StatusBarIconList();
ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
mCommandQueue = new CommandQueue(this, iconList);
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
int[] switches = new int[7];
ArrayList<IBinder> binders = new ArrayList<IBinder>();
try {
mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,
switches, binders);
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
disable(switches[0]);
setSystemUiVisibility(switches[1]);
topAppWindowChanged(switches[2] != 0);
// StatusBarManagerService has a back up of IME token and it's restored here.
setImeWindowStatus(binders.get(0), switches[3], switches[4]);
setHardKeyboardStatus(switches[5] != 0, switches[6] != 0);
// Set up the initial icon state
int N = iconList.size();
int viewIndex = 0;
for (int i=0; i<N; i++) {
StatusBarIcon icon = iconList.getIcon(i);
if (icon != null) {
addIcon(iconList.getSlot(i), i, viewIndex, icon);
viewIndex++;
}
}
// Set up the initial notification state
N = notificationKeys.size();
if (N == notifications.size()) {
for (int i=0; i<N; i++) {
addNotification(notificationKeys.get(i), notifications.get(i));
}
} else {
Log.wtf(TAG, "Notification list length mismatch: keys=" + N
+ " notifications=" + notifications.size());
}
// Put up the view
final int height = getStatusBarHeight();
mStatusBarHeight = height;
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
height,
WindowManager.LayoutParams.TYPE_STATUS_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.OPAQUE);
// the status bar should be in an overlay if possible
final Display defaultDisplay
= ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
if (ActivityManager.isHighEndGfx(defaultDisplay)) {
lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
lp.gravity = getStatusBarGravity();
lp.setTitle("StatusBar");
lp.packageName = mContext.getPackageName();
lp.windowAnimations = R.style.Animation_StatusBar;
mStatusBarLayoutParams = lp;
mStatusBarView = sb;
WindowManagerImpl.getDefault().addView(sb, lp);
if (SPEW) {
Slog.d(TAG, "Added status bar view: gravity=0x" + Integer.toHexString(lp.gravity)
+ " icons=" + iconList.size()
+ " disabled=0x" + Integer.toHexString(switches[0])
+ " lights=" + switches[1]
+ " menu=" + switches[2]
+ " imeButton=" + switches[3]
);
}
mDoNotDisturb = new DoNotDisturb(mContext);
}
protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
View vetoButton = row.findViewById(R.id.veto);
if (n.isClearable()) {
final String _pkg = n.pkg;
final String _tag = n.tag;
final int _id = n.id;
vetoButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
mBarService.onNotificationClear(_pkg, _tag, _id);
} catch (RemoteException ex) {
// system process is dead if we're here.
}
}
});
vetoButton.setVisibility(View.VISIBLE);
} else {
vetoButton.setVisibility(View.GONE);
}
return vetoButton;
}
@Override
public void onReceive(String action) {
Log.d(TAG, "*** onReceive(), action = " + action + " ***");
if (SystemUIService.ACTION_DISPLAY_STATUS_BAR.equals(action)) {
mStatusBarLayoutParams.height = mStatusBarHeight;
WindowManagerImpl.getDefault().updateViewLayout(mStatusBarView,
mStatusBarLayoutParams);
} else if (SystemUIService.ACTION_HIDE_STATUS_BAR.equals(action)) {
mStatusBarLayoutParams.height = 0;
WindowManagerImpl.getDefault().updateViewLayout(mStatusBarView,
mStatusBarLayoutParams);
}
}
}[/mw_shl_code]
至此,在SystemUI.apk 中的改动全部完成。
frameworks/base/packages/SystemUI
在SystemUI 启动流程 一文中对 SystemUI 有详细的介绍,在这里就不再赘述,各位看官可移步了解之。废话不多说,开始回到正题。我们最终想要得到的效果,就是完全隐藏平板上的导航栏,没有占用任何显示空间。
SystemUI 的启动过程(平板部分)
在Android 4.0 ICS SystemUI浅析——SystemUI启动流程这篇文章中做了详细的介绍,挑几个重要的步骤来讲:
1.
SystemServer 在开机之后启动SystemUIService
[mw_shl_code=java,true]static final void startSystemUi(Context context) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.systemui",
"com.android.systemui.SystemUIService"));
Slog.d(TAG, "Starting service: " + intent);
context.startService(intent);
} [/mw_shl_code]
2. SystemUIService 启动两个继承自 SystemUI 的 "service", 其中 导航栏的类是根据屏幕分辨率来判断的
[mw_shl_code=java,true] // Pick status bar or system bar.
IWindowManager wm = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
try {
Log.d(TAG, "*** canStatusBarHide = " + wm.canStatusBarHide() + " ***");
SERVICES[0] = wm.canStatusBarHide()
? R.string.config_statusBarComponent
: R.string.config_systemBarComponent;
} catch (RemoteException e) {
Slog.w(TAG, "Failing checking whether status bar can hide", e);
}
final int N = SERVICES.length;
mServices = new SystemUI[N];
for (int i=0; i<N; i++) {
Class cl = chooseClass(SERVICES );
Slog.d(TAG, "loading: " + cl);
try {
mServices = (SystemUI)cl.newInstance();
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InstantiationException ex) {
throw new RuntimeException(ex);
}
mServices.mContext = this;
Slog.d(TAG, "running: " + mServices);
mServices.start();
}
[/mw_shl_code]
如果是平板(wm.canStatusBarHide() = false)则启用 com.android.systemui.statusbar.tablet.TabletStatusBar,这也是我们接下来研究的重点。因为手机已经可以做到完全全屏。
继续跟踪,发现 PhoneStatusBar 和 TabletStatusBar 都继承自 StatusBar, 在 StatusBar 中有一个非常重要的抽象函数 makeStatusBarView(),没错,看名字就可以知道,就是它初始化了SystemUI的主要部分(手机的是 NavigationBar 和 StatusBar, 平板的是 Combination bar). 不管是手机还是平板,都是通过这个函数来生成自己的导航栏,被 StatusBar 调用,具体代码如下:
[mw_shl_code=java,true]public void start() {
// First set up our views and stuff.
View sb = makeStatusBarView();
// Connect in to the status bar manager service
StatusBarIconList iconList = new StatusBarIconList();
ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
mCommandQueue = new CommandQueue(this, iconList);
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
int[] switches = new int[7];
ArrayList<IBinder> binders = new ArrayList<IBinder>();
try {
mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,
switches, binders);
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
disable(switches[0]);
setSystemUiVisibility(switches[1]);
topAppWindowChanged(switches[2] != 0);
// StatusBarManagerService has a back up of IME token and it's restored here.
setImeWindowStatus(binders.get(0), switches[3], switches[4]);
setHardKeyboardStatus(switches[5] != 0, switches[6] != 0);
// Set up the initial icon state
int N = iconList.size();
int viewIndex = 0;
for (int i=0; i<N; i++) {
StatusBarIcon icon = iconList.getIcon(i);
if (icon != null) {
addIcon(iconList.getSlot(i), i, viewIndex, icon);
viewIndex++;
}
}
// Set up the initial notification state
N = notificationKeys.size();
if (N == notifications.size()) {
for (int i=0; i<N; i++) {
addNotification(notificationKeys.get(i), notifications.get(i));
}
} else {
Log.wtf(TAG, "Notification list length mismatch: keys=" + N
+ " notifications=" + notifications.size());
}
// Put up the view
final int height = getStatusBarHeight();
mStatusBarHeight = height;
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
height,
WindowManager.LayoutParams.TYPE_STATUS_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.OPAQUE);
// the status bar should be in an overlay if possible
final Display defaultDisplay
= ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
if (ActivityManager.isHighEndGfx(defaultDisplay)) {
lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
lp.gravity = getStatusBarGravity();
lp.setTitle("StatusBar");
lp.packageName = mContext.getPackageName();
lp.windowAnimations = R.style.Animation_StatusBar;
mStatusBarLayoutParams = lp;
mStatusBarView = sb;
WindowManagerImpl.getDefault().addView(sb, lp);
if (SPEW) {
Slog.d(TAG, "Added status bar view: gravity=0x" + Integer.toHexString(lp.gravity)
+ " icons=" + iconList.size()
+ " disabled=0x" + Integer.toHexString(switches[0])
+ " lights=" + switches[1]
+ " menu=" + switches[2]
+ " imeButton=" + switches[3]
);
}
mDoNotDisturb = new DoNotDisturb(mContext);
}
[/mw_shl_code]
通过上面这段代码,可以看到,在调用makeStatusBarView生成View sb 之后,通过 WindowManagerImpl.getDefault().addView(sb, lp); 将其显示到屏幕之上。追踪到这里,我想大多数同学应该知道怎么去做导航栏的隐藏了。。
![](http://www.eoeandroid.com/static/image/smiley/tutu/20.gif)
接下来的问题,就是要找个合适的位子,添加上我们的这段代码,在什么地方调用updateView才是最合适的呢?
全屏实现(4.0平板)
由于 StatusBar 是被 SystemUIService 调用的,而且仅仅执行 start() 函数,而 StatusBar 是个普通的Java类,没有Context上下文环境,不具备接收事件的能力(这里可能描述得不够清楚,主要就是说应用层发送的广播啊,startService之类的方法都不被StatusBar接收到。因为我们需要在应用层调用相关cmd来控制导航栏的显示/隐藏)。
我们会发现,SystemUIService 是个普通的 service 能够使用到组件之间的通信之中,并且StatusBar也是在这里被调起来的。开始动工吧,能想到的最简单的调用顺序就是 :
1. 扩展 SystemUI, 添加多一个抽象函数 onReceive(String action)
2. 在 SystemUIService 中注册一个 BroadcastReceiver 监听我们定制的两个 action (隐藏/显示)
3. 在 BroadcastReceive 函数中,调用 SystemUI 的 onReceive函数
4. 在 StatusBar 的 onReceive 函数中处理导航栏的显示/隐藏。
在 SystemUI 中添加抽象函数(frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUI.java)
[mw_shl_code=java,true]public abstract class SystemUI {
public Context mContext;
public abstract void start();
public abstract void onReceive(String action);
protected void onConfigurationChanged(Configuration newConfig) {
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
}
}[/mw_shl_code]
在 SystemUIService 中注册 BroadcastReceiver (frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java)
[mw_shl_code=java,true]@Override
public void onCreate() {
// Pick status bar or system bar.
IWindowManager wm = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
try {
Log.d(TAG, "*** canStatusBarHide = " + wm.canStatusBarHide() + " ***");
SERVICES[0] = wm.canStatusBarHide()
? R.string.config_statusBarComponent
: R.string.config_systemBarComponent;
} catch (RemoteException e) {
Slog.w(TAG, "Failing checking whether status bar can hide", e);
}
final int N = SERVICES.length;
mServices = new SystemUI[N];
for (int i=0; i<N; i++) {
Class cl = chooseClass(SERVICES);
Slog.d(TAG, "loading: " + cl);
try {
mServices = (SystemUI)cl.newInstance();
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InstantiationException ex) {
throw new RuntimeException(ex);
}
mServices.mContext = this;
Slog.d(TAG, "running: " + mServices);
mServices.start();
}
// register broadcast receiver
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_DISPLAY_STATUS_BAR);
intentFilter.addAction(ACTION_HIDE_STATUS_BAR);
registerReceiver(mStatusBarReceiver, intentFilter);
}[/mw_shl_code]
在 onReceive 中分发事件(frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java)
[mw_shl_code=java,true]BroadcastReceiver mStatusBarReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null && !TextUtils.isEmpty(intent.getAction())) {
String action = intent.getAction();
final int N = SERVICES.length;
for (int i = 0; i < N; i++) {
Slog.d(TAG, "invoke: " + mServices + "'s onReceive()");
mServices.onReceive(action);
}
}
}
};[/mw_shl_code]
在StatusBar中处理消息(frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java)
[mw_shl_code=java,true]public abstract class StatusBar extends SystemUI implements CommandQueue.Callbacks {
static final String TAG = "StatusBar";
private static final boolean SPEW = false;
protected CommandQueue mCommandQueue;
protected IStatusBarService mBarService;
// Up-call methods
protected abstract View makeStatusBarView();
protected abstract int getStatusBarGravity();
public abstract int getStatusBarHeight();
public abstract void animateCollapse();
private DoNotDisturb mDoNotDisturb;
private View mStatusBarView;
private int mStatusBarHeight;
private WindowManager.LayoutParams mStatusBarLayoutParams;
public void start() {
// First set up our views and stuff.
View sb = makeStatusBarView();
// Connect in to the status bar manager service
StatusBarIconList iconList = new StatusBarIconList();
ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
mCommandQueue = new CommandQueue(this, iconList);
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
int[] switches = new int[7];
ArrayList<IBinder> binders = new ArrayList<IBinder>();
try {
mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,
switches, binders);
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
disable(switches[0]);
setSystemUiVisibility(switches[1]);
topAppWindowChanged(switches[2] != 0);
// StatusBarManagerService has a back up of IME token and it's restored here.
setImeWindowStatus(binders.get(0), switches[3], switches[4]);
setHardKeyboardStatus(switches[5] != 0, switches[6] != 0);
// Set up the initial icon state
int N = iconList.size();
int viewIndex = 0;
for (int i=0; i<N; i++) {
StatusBarIcon icon = iconList.getIcon(i);
if (icon != null) {
addIcon(iconList.getSlot(i), i, viewIndex, icon);
viewIndex++;
}
}
// Set up the initial notification state
N = notificationKeys.size();
if (N == notifications.size()) {
for (int i=0; i<N; i++) {
addNotification(notificationKeys.get(i), notifications.get(i));
}
} else {
Log.wtf(TAG, "Notification list length mismatch: keys=" + N
+ " notifications=" + notifications.size());
}
// Put up the view
final int height = getStatusBarHeight();
mStatusBarHeight = height;
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
height,
WindowManager.LayoutParams.TYPE_STATUS_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.OPAQUE);
// the status bar should be in an overlay if possible
final Display defaultDisplay
= ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
if (ActivityManager.isHighEndGfx(defaultDisplay)) {
lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
lp.gravity = getStatusBarGravity();
lp.setTitle("StatusBar");
lp.packageName = mContext.getPackageName();
lp.windowAnimations = R.style.Animation_StatusBar;
mStatusBarLayoutParams = lp;
mStatusBarView = sb;
WindowManagerImpl.getDefault().addView(sb, lp);
if (SPEW) {
Slog.d(TAG, "Added status bar view: gravity=0x" + Integer.toHexString(lp.gravity)
+ " icons=" + iconList.size()
+ " disabled=0x" + Integer.toHexString(switches[0])
+ " lights=" + switches[1]
+ " menu=" + switches[2]
+ " imeButton=" + switches[3]
);
}
mDoNotDisturb = new DoNotDisturb(mContext);
}
protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
View vetoButton = row.findViewById(R.id.veto);
if (n.isClearable()) {
final String _pkg = n.pkg;
final String _tag = n.tag;
final int _id = n.id;
vetoButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
mBarService.onNotificationClear(_pkg, _tag, _id);
} catch (RemoteException ex) {
// system process is dead if we're here.
}
}
});
vetoButton.setVisibility(View.VISIBLE);
} else {
vetoButton.setVisibility(View.GONE);
}
return vetoButton;
}
@Override
public void onReceive(String action) {
Log.d(TAG, "*** onReceive(), action = " + action + " ***");
if (SystemUIService.ACTION_DISPLAY_STATUS_BAR.equals(action)) {
mStatusBarLayoutParams.height = mStatusBarHeight;
WindowManagerImpl.getDefault().updateViewLayout(mStatusBarView,
mStatusBarLayoutParams);
} else if (SystemUIService.ACTION_HIDE_STATUS_BAR.equals(action)) {
mStatusBarLayoutParams.height = 0;
WindowManagerImpl.getDefault().updateViewLayout(mStatusBarView,
mStatusBarLayoutParams);
}
}
}[/mw_shl_code]
至此,在SystemUI.apk 中的改动全部完成。