2种方式:
1.修改SystemUI Navigation部分中的aidl部分。
Navigation部分默认有3个虚拟按键:返回,HOME,菜单,例如需要隐藏最左边的返回按键。
首先,在IStatusBarService.aidl部分增加一个函数void updateNaviState(String s)。
//frameworks\base\core\java\com\android\internal\statusbar\IStatusBarService.aidl
interface IStatusBarService{
void expandNotificationsPanel();
void collapsePanels();
void togglePanel();
void disable(int what, IBinder token, String pkg);
.....
void updateNaviState(String s);
}
然后,在IStatusBar.aidl中添加另一个函数updateNavibarState(String s)。
//frameworks\base\core\java\com\android\internal\statusbar\IStatusBar.aidl
oneway interface IStatusBar{
void setIcon(String slot, in StatusBarIcon icon);
void removeIcon(String slot);
.....
void updateNavibarState(String s)
}
然后,在CommandQueue类的Callbacks接口中添加函updateNavibarState。
CommandQueue继承自IStatusBar.Stub,IStatusBar.Stub是IStatusBar.aidl自动生成的java文件。在CommandQueue中定义了一个Callbacks接口,而IStatusBar接口中的定义都是通过Callbacks来调用的。
//vendor\mediatek\proprietary\packages\xx\xx\src\com\android\systemui\statusbar\CommandQueue.java
public class CommandQueue extends IStatusBar.Stub {
..........
public interface Callbacks {
default void setIcon(String slot, StatusBarIcon icon) { }
default void removeIcon(String slot) { }
.......
default void updateNavibarState(String s){ }
}
..........
}
然后就是Callbacks 的使用,Callbacks 是在StatusBar 中使用的。StatusBar继承Callbacks后重写updateNavibarState。
//vendor\mediatek\proprietary\packages\xx\xx\src\com\android\systemui\statusbar\phone\StatusBar.java
public class StatusBar extends SystemUI implements ...,CommandQueue.Callbacks{
.......
@Override
public void updateNavibarState(String s) {
getNavigationBarView().updateNaviSate(s);
}
.......
}
NavigationBarView返回的是NavigationBarView,需要在NavigationBarView中定义一个updateNaviSate(String s)函数。
首先,修改navigation_bar.xml文件。原文件如下所示:
//vendor\mediatek\proprietary\packages\xxx\xxx\res\layout\navigation_bar.xml
将NavigationBarInflaterView隐藏并自定义一个LinearLayout,用这个去实现隐藏部分按钮。由于NavigationBarInflaterView不容易更改因此直接隐藏通过自定义控件去实现该功能。
修改完后,再来定NavigationBarView中的内容。
//vendor\mediatek\proprietary\packages\xx\xx\src\com\android\systemui\statusbar\phone\NavigationBarView.java
public class NavigationBarView extends FrameLayout implements PluginListener {
.........
private LinearLayout navigationBackground;
private TextView leftNavTxtView;
private TextView rightNavTxtView;
private TextView midNavTxtView;
.........
public NavigationBarView(Context context, AttributeSet attrs) {
.........
navigationBackground = findViewById(R.id.navigation_background);
leftNavTxtView = findViewById(R.id.navigation_menu);
rightNavTxtView = findViewById(R.id.navigation_contact);
midNavTxtView = findViewById(R.id.navigation_mid);
.........
public void updateNaviSate(String s){
leftNavTxtView.setText(s);
}
.........
}
在StatusBarManager中新增一个函数,调用IStatusBarService中的updateNaviState(String s)。
//frameworks\base\core\java\android\app\StatusBarManager.java
@SystemService(Context.STATUS_BAR_SERVICE)
public class StatusBarManager {
.....
public void updateNaviState(String s) {
try {
final IStatusBarService svc = getService();
if (svc != null) {
svc.updateNaviState(s);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
.....
}
在StatusBarManagerService 中增加对应的函数调用IStatusBar
中的updateNavibarState
。
//frameworks\base\services\core\java\com\android\server\statusbar\StatusBarManagerService.java
public class StatusBarManagerService extends IStatusBarService.Stub {
.....
@Override
public void updateNaviState(String s){
if (mBar != null) {
try {
mBar.updateNavibarState(s);
} catch (RemoteException ex) {
}
}
}
.....
}
最后使用就是在Activity 中定义updateNaviState函数。
//frameworks\base\core\java\android\app\Activity.java
public class Activity extends xxx{
..............
public void updateNaviState(String s){
getSystemService(StatusBarManager.class).updateNaviState(s);
}
}
然后在需要使用的时候继承这个Activity 即可。
public class MainActivity extends Activity {
...........
@Override
protected void onResume() {
super.onResume();
updateNaviState("");
}
...........
}
由于想隐藏最左边的控件,所以直接传了一个空的string。这里最好是在onResume中调用。
如果想隐藏整个导航栏,那么可以通过navigationBackground.setBackgroundColor设置为透明从而隐藏。
这一部分是通过获取service manage,调用service,并且增加对应回调函数去实现,不禁让我思考,到底什么是client,之前aidl的使用经历让我觉得需要有aidl文件并且调用service才算client,但是照目前来看,调用了service,可以算client(也就是说Activity 算client)。对于binder的实质,我始终没有弄清楚。
2.广播
广播的方式缺点是有延时,适用于没有界面的时候。
如果是使用广播的方式的话,StatusBar 中有一个mBaseBroadcastReceiver ,需要修改这一部分。
//vendor\mediatek\proprietary\packages\xx\xx\src\com\android\systemui\statusbar\phone\StatusBar.java
public class StatusBar extends SystemUI implements ...,CommandQueue.Callbacks{
public static final String ACTION_NAVI= "xxx.xxx.xxx.action.systemui.navi";
...........
internalFilter.addAction(BANNER_ACTION_SETUP);
internalFilter.addAction(ACTION_NAVI);
...........
private final BroadcastReceiver mBaseBroadcastReceiver = new BroadcastReceiver(){
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_USER_SWITCHED.equals(action)){
...........
}
...........
else if (ACTION_NAVI.equals(intent.getAction())){
getNavigationBarView().updateNaviSate( intent.getStringExtra("left"));
}
}
}
...........
}
使用的话,发送广播即可。
String ACTION_NAVI= "xxx.xxx.xxx.action.systemui.navi";
Intent intent=new Intent(ACTION_NAVI);
intent.putExtra("left","");
sendBroadcast(intent);
参考链接:
Android系统服务(SystemService)简介
Android 状态栏, 标题栏, 导航栏,系统栏,应用栏的区别和解释
Android 8.1平台SystemUI虚拟导航键加载流程解析
IStatusBarService 的AIDL实现与调用