admin 管理员组

文章数量: 1184232

系统服务

SystemUI总结

android 下拉状态栏(SystemUI)常见修改记录

systemui整体介绍

StatusBar 系统图标

Notification流程
.html
SystemUI9.0系统应用图标加载流程

Android 8.0 SystemUI(四):二说顶部 StatusBar 信号等图标

导航栏

SystemUI分类
状态栏StatusBar:通知消息提示和状态展现
导航栏NavigationBar:返回,HOME,Recent
锁屏界面KeyGuard:锁屏模块可以看做单独的应用,提供基本的手机个人隐私保护
最近任务Recents:近期应用管理,以堆叠栈的形式展现。
通知栏Notification Panel:QuickSettings和Notification展示系统或应用通知内容。提供快速系统设置开关
音量调节对话框VolumeUI:来用展示或控制音量的变化:媒体音量、铃声音量与闹钟音量
截屏界面ScreenShot::长按电源键+音量下键后截屏,用以展示截取的屏幕照片/内容
电源管理PowerUI:主要处理和Power相关的事件,比如省电模式切换、电池电量变化和开关屏事件等
RingtonePlayer:铃声播放
StackDivider:控制管理分屏
PipUI:提供对于画中画模式的管理

9.0源码目录:

SystemUI启动流程
由init进程->Zygote进程->SystemServer进程->systemui启动

按电源键,系统上电,从固定地址加载固化在ROM的bootloader代码到RAM中执行,bootloader引导程序将os拉起,系统拉起后进行初始化和解析启动init进程。

startOtherServices()中,通过调用AMS的systemReady()方法通知AMS准备就绪。systemReady()拥有一个名为goingCallback的Runnable实例作为参数,当AMS完成systemReady()处理后会回调Runnable.run()

ps:frameworks/base/service/java/com/android/server/SystemServer.javapublic void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) {
......
if (mSystemReady) {// If we're done calling all the receivers, run the next "boot phase" passed in// by the SystemServerif (goingCallback != null) {goingCallback.run();}return;
}
......
}

AMS在回调中启动SystemUI

mActivityManagerService.systemReady(() -> {
......startSystemUi(context, windowManagerF);
......
}
static final void startSystemUi(Context context, WindowManagerService windowManager) {Intent intent = new Intent();intent.setComponent(new ComponentName("com.android.systemui","com.android.systemui.SystemUIService"));intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);//Slog.d(TAG, "Starting service: " + intent);context.startServiceAsUser(intent, UserHandle.SYSTEM);windowManager.onSystemUiStarted();
}

在开启服务后,WMS回调SystemUI已开启

ps:frameworks/base/service/core/java/com/android/server/wm/WindowManagerService.javapublic void onSystemUiStarted() {mPolicy.onSystemUiStarted();
}ps:frameworks/base/core/java/android/view/WindowManagerPolicy.javavoid onSystemUiStarted();

SystemUIService继承了Service

public class SystemUIService extends Service {
......
@Override
public void onCreate() {super.onCreate();((SystemUIApplication) getApplication()).startServicesIfNeeded();......
}
......
}
ps:frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.javapublic void startServicesIfNeeded() {String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents);android.util.Log.e("sunyang", "SystemUIApplication startServicesIfNeeded names :" + names);startServicesIfNeeded(names);
}private void startServicesIfNeeded(String[] services) {
......
try {cls = Class.forName(clsName);mServices[i] = (SystemUI) cls.newInstance();
}
......
mServices[i].mContext = this;
mServices[i].mComponents = mComponents;
if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
android.util.Log.e("sunyang", "SystemUIApplication startServicesIfNeeded running :" + mServices[i]);
mServices[i].start();
......
}loading: com.android.systemui.Dependencyloading: com.android.systemui.util.NotificationChannelsloading: com.android.systemui.statusbar.CommandQueue$CommandQueueStartloading: com.android.systemui.keyguard.KeyguardViewMediatorloading: com.android.systemui.recents.Recentsloading: com.android.systemui.volume.VolumeUIloading: com.android.systemui.stackdivider.Dividerloading: com.android.systemui.SystemBarsloading: com.android.systemui.usb.StorageNotificationloading: com.android.systemui.power.PowerUIloading: com.android.systemui.media.RingtonePlayerloading: com.android.systemui.keyboard.KeyboardUIloading: com.android.systemui.pip.PipUIloading: com.android.systemui.shortcut.ShortcutKeyDispatcherloading: com.android.systemui.VendorServicesloading: com.android.systemui.util.leak.GarbageMonitor$Serviceloading: com.android.systemui.LatencyTesterloading: com.android.systemui.globalactions.GlobalActionsComponentloading: com.android.systemui.ScreenDecorationsloading: com.android.systemui.fingerprint.FingerprintDialogImplloading: com.android.systemui.SliceBroadcastRelayHandlerloading: com.android.systemui.screenswitch.ScreenSwitchUIloading: com.android.systemui.presssensor.PressSensorUI

统一接口

ps:frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUI.javapublic abstract class SystemUI implements SysUiServiceProvider {
...
public abstract void start();
处理系统状态变化的回调,这里的状态变化包括:时区变更,字体大小变更,输入模式变更,屏幕大小变更,屏幕方向变更等
protected void onConfigurationChanged(Configuration newConfig) {}
用来将模块的内部状态dump到输出流中,这个方法主要是辅助调试所用。开发者可以在开发过程中,通过adb shell执行dump来了解系统的内部状态
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {}
系统启动完成的回调方法
protected void onBootCompleted() {}
...
}

状态栏:StatusBar Notification QuickSettings

ps:frameworks/base/packages/SystemUI/src/com/android/systemui/SystemBars.java@Override
public void start() {if (DEBUG) Log.d(TAG, "start");createStatusBarFromConfig(); 创建了状态栏
}ps:frameworks/base/packages/SystemUI/res/value/config.xml
config_statusBarComponent com.android.systemui.statusbar.phone.StatusBarprivate void createStatusBarFromConfig() {if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");final String clsName = mContext.getString(R.string.config_statusBarComponent);if (clsName == null || clsName.length() == 0) {throw andLog("No status bar component configured", null);}Class<?> cls = null;try { 类加载器加载对应类cls = mContext.getClassLoader().loadClass(clsName);} catch (Throwable t) {throw andLog("Error loading status bar component: " + clsName, t);}try { 反射创建对象mStatusBar = (SystemUI) cls.newInstance();} catch (Throwable t) {throw andLog("Error creating status bar component: " + clsName, t);}mStatusBar.mContext = mContext;mStatusBar.mComponents = mComponents;mStatusBar.start();if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
}

调用StatusBar的start() 状态栏和导航栏的启动过程
1、获取IStatusBarService,IStatusBarService是运行于system_server的系统服务,它接受操作状态栏/导航栏的请求并将其转发给StatusBar,为了保证SystemUI意外退出不会发生信息丢失,IStatusBarService保存了所有状态栏和导航栏进行显示或处理的信息副本
2、将一个继承自IStatusBar.Stub的CommandQueue的实例注册到IStatusBarService以建立通信,并将信息副本取回
3、创建状态栏与导航栏的窗口createAndAddWindows()创建statusbar
4、使用从IStatusBarService取回的信息副本

7.1.3 理解IStatusBarService
public class StatusBarManagerService extends IStatusBarService.Stub {}
与WMS、IMS等系统服务一样,StatusBarManagerService 在ServerThread中创建

        if (!isWatch) {traceBeginAndSlog("StartStatusBarManagerService");try {statusBar = new StatusBarManagerService(context, wm);ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);} catch (Throwable e) {reportWtf("starting StatusBarManagerService", e);}traceEnd();}构造方法:public StatusBarManagerService(Context context, WindowManagerService windowManager) {mContext = context;mWindowManager = windowManager;LocalServices.addService(StatusBarManagerInternal.class, mInternalService);LocalServices.addService(GlobalActionsProvider.class, mGlobalActionsProvider);}获取保存在其中的副本
StatusBarManagerService.registerStatusBar
ps:frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.javapublic class StatusBar extends SystemUI implements DemoMode,DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter {@Overridepublic void start() {.........//mCommandQueue是CommandQueue类的一个实例,CommandQueue继承自IStatusBar.Stub,因此它是IStatusBar//的Bn端。在完成注册后,这个Binder对象的Bp端将会保存在IStatusBarService中。// Connect in to the status bar manager servicemCommandQueue = getComponent(CommandQueue.class);mCommandQueue.addCallbacks(this);//switches存储了一些杂项:禁用功能列表,SystemUIVisiblity,是否在导航栏中显示虚拟的菜单键,输入法窗口//是否可见,输入法窗口是否消费BACK键,是否接入了实体键盘,实体键盘是否被启用int[] switches = new int[9];ArrayList<IBinder> binders = new ArrayList<>();ArrayList<String> iconSlots = new ArrayList<>();ArrayList<StatusBarIcon> icons = new ArrayList<>();Rect fullscreenStackBounds = new Rect();Rect dockedStackBounds = new Rect();try {//2 向IStatusBarService进行注册,并获取保存在IStatusBarService中的信息副本mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,fullscreenStackBounds, dockedStackBounds);} catch (RemoteException ex) {// If the system process isn't there we're doomed anyway.}//3 创建状态栏与导航栏的窗口createAndAddWindows(); 创建statusbar......mLockscreenUserManager.setUpWithPresenter(this, mEntryManager);//禁用某些功能mCommandQueue.disable(switches[0], switches[6], false /* animate */);//设置SystemUiVisibilitysetSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,fullscreenStackBounds, dockedStackBounds);//设置菜单键的可见性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], switches[5] != 0);//依次向系统状态栏区添加状态图标// Set up the initial icon stateint N = iconSlots.size();for (int i=0; i < N; i++) {mCommandQueue.setIcon(iconSlots.get(i), icons.get(i));}//导航栏和状态栏启动完成...}
}

解析步骤2
mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
fullscreenStackBounds, dockedStackBounds);

    @Overridepublic void registerStatusBar(IStatusBar bar, List<String> iconSlots,List<StatusBarIcon> iconList, int switches[], List<IBinder> binders,Rect fullscreenStackBounds, Rect dockedStackBounds) {//权限检查,安全为避免其他应用调用,因此调用者必须具有系统级权限android.Manifest.permission.STATUS_BAR_SERVICEenforceStatusBarService();//1、将bar参数保存在mBar成员中,mBar类型IStatusBar,就是CommandQueue的Bp端,后面StatusBarManagerService通过mBar与StatusBar 通信Slog.i(TAG, "registerStatusBar bar=" + bar);mBar = bar;try {mBar.asBinder().linkToDeath(new DeathRecipient() {@Overridepublic void binderDied() {mBar = null;notifyBarAttachChanged();}}, 0);} catch (RemoteException e) {}notifyBarAttachChanged();//2、接下来依次为调用者返回信息副本,系统状态区的图标列表synchronized (mIcons) {for (String slot : mIcons.keySet()) {iconSlots.add(slot);iconList.add(mIcons.get(slot));}}//switches中的杂项synchronized (mLock) {switches[0] = gatherDisableActionsLocked(mCurrentUserId, 1);switches[1] = mSystemUiVisibility;switches[2] = mMenuVisible ? 1 : 0;switches[3] = mImeWindowVis;switches[4] = mImeBackDisposition;switches[5] = mShowImeSwitcher ? 1 : 0;switches[6] = gatherDisableActionsLocked(mCurrentUserId, 2);switches[7] = mFullscreenStackSysUiVisibility;switches[8] = mDockedStackSysUiVisibility;binders.add(mImeToken);fullscreenStackBounds.set(mFullscreenStackBounds);dockedStackBounds.set(mDockedStackBounds);}}

StatusBarManagerService的工作方式
当他接受到操作状态栏和导航栏的请求时,首先将请求信息保存到副本中,然后将mBar发送给StatusBar
1、StatusBarManagerService是状态栏和导航栏在system_server中的代理。所有对状态栏和导航栏的需求都通过此实例操作
2、StatusBarManagerService保存了状态栏和导航栏所需的信息副本,用于在SystemUI意外退出后恢复

设置系统状态区图标

    @Overridepublic void setIcon(String slot, String iconPackage, int iconId, int iconLevel,String contentDescription) {enforceStatusBar();synchronized (mIcons) {StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.SYSTEM, iconId,iconLevel, 0, contentDescription);//Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon);//1、将图标信息保存在副本中mIcons.put(slot, icon);//2、将设置请求发送给StatusBarif (mBar != null) {try {mBar.setIcon(slot, icon);} catch (RemoteException ex) {}}}}

7.1.4 SystemUI的体系结构

1、SystemUIService,一个普通的android服务,它以一个容器的角色运行于SystemUI进程中。在其内部运行着多个服务,包括StatusBar
2、IStatusBarService,即系统服务StatusBarManagerService是状态栏和导航栏向外界提供服务的接口,运行在system_server进程中
3、IStatusBar,即SystemUI中的CommandQueue是联系StatusBarManagerService与StatusBar的桥梁
4、SystemUI还包括ImageWallpaper等功能实现。通过startService、startActivity

布局图

StatusBar.java
super_status_bar.xml@+id/status_bar_containerstatus_bar.xml@+id/notification_lights_out@+id/status_bar_contents@+id/clock@+id/netspeedstatus_bar_expanded.xmlqs_framebrightness_mirror.xml顶部状态栏
status_bar.xml@+id/notification_lights_out@+id/status_bar_contentsheads_up_status_bar_layout@+id/status_bar_left_side@+id/clock@+id/notification_icon_area@+id/system_icon_area@+id/netspeedsystem_icons@+id/notification_lights_out
一个ImageView,一般不可见。在SystemUIVisiblity中有一个名为SYSTEM_UI_FLAG_LOW_PROFILE的标记。当应用程序希望用户注意力集中在它所显示的内容上,可以在其SystemUIVisiblity中添加这一标记。SYSTEM_UI_FLAG_LOW_PROFILE会使状态栏和导航栏进入低辨识度模式。低辨识度模式下状态栏不会显示任何信息,只是在黑色背景中显示一个灰色圆点。这个黑色圆点为notification_lights_out@+id/status_bar_contents
一个LinearLayout,状态栏上各种信息的显示场所@+id/status_bar_contentsheads_up_status_bar_layout@+id/status_bar_left_side@+id/clock@+id/notification_icon_area@+id/system_icon_area@+id/netspeedsystem_icons@+id/notification_icon_area
通知信息
@+id/system_icon_area

7.2 深入理解状态栏
作为一个将所有信息集中显示的场所,状态栏对需要显示的信息分为以下5种
1、通知信息:它可以在状态栏左侧显示一个图标以引起用户的注意,并在下拉栏中为用户显示更加详细的信息。这是状态栏所能提供的信息显示服务之中最灵活的一种功能。它对信息种类以及来源没有做任何限制。使用者可以通过StatusBarManagerService所提供的接口向状态栏中添加或移除一条通知信息
2、时间信息:显示在状态栏最右侧的一个小型数字时钟,是一个名为Clock的继承自TextView的控件。它监听了几个和时间相关的广播:ACTION_TIME_TICK、ACTION_TIME_CHANGED、ACTION_TIMEZONE_CHANGED、ACTION_CONFIGURATION_CHANGED。当其中一个广播到来时从Calendar类中获取当前的系统时间,然后进行字符串格式化后显示出来。时间信息的维护工作在状态栏内部完成,因此外界无法通过api修改时间信息和显示行为
3、电量信息:显示在数字时钟左侧的电池图标,用于提示当前的电量情况。它是一个被BatteryController类所管理的ImageView。BatteryController通过监听android.action.BATTERY_CHANGED广播从BetteryService中获取电量信息,并根据电量信息 选择一个合适的电池图标显示在ImageView上。同时间信息一样,这也是在状态栏内部维护的,外界无法干预状态栏对电量信息的显示行为
4、信号信息:显示在电量信息的左侧的一系列ImageView,用于显示系统当前的WIFI、移动网络的信息的范畴。他们被NetworkController类维护,NetworkController监听一系列与信号相关的广播如WIFI_STATE_CHANGED_ACTION、ACTION_SIM_STATE_CHANGED、ACTION_AIRPLANE_MODE_CHANGED等,并在这些广播到来时显示、更改或移除相关的ImageView
5、系统状态图标区:这个区域用一系列图标标识系统当前的状态,位于信号左侧。StatusBarManagerService通过setIcon() 为外界提供修改系统状态图标区的途径

7.2.1 状态栏窗口的创建与控件数结构
1 状态栏窗口的创建

public void createAndAddWindows() {addStatusBarWindow();
}private void addStatusBarWindow() {makeStatusBarView();mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);mRemoteInputManager.setUpWithPresenter(this, mEntryManager, this,new RemoteInputController.Delegate() {public void setRemoteInputActive(NotificationData.Entry entry,boolean remoteInputActive) {mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);entry.row.notifyHeightChanged(true /* needsAnimation */);updateFooter();}public void lockScrollTo(NotificationData.Entry entry) {mStackScroller.lockScrollTo(entry.row);}public void requestDisallowLongPressAndDismiss() {mStackScroller.requestDisallowLongPress();mStackScroller.requestDisallowDismiss();}});mRemoteInputManager.getController().addCallback(mStatusBarWindowManager);4 通过 mStatusBarWindowManager.add创建状态栏的窗口mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}2 为状态栏创建WindowManager.LayoutParamspublic void add(View statusBarView, int barHeight) {// Now that the status bar window encompasses the sliding panel and its// translucent backdrop, the entire thing is made TRANSLUCENT and is// hardware-accelerated.mLp = new WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,//状态栏的宽度为充满整个屏幕宽度barHeight,//getStatusBarHeight()WindowManager.LayoutParams.TYPE_STATUS_BAR,//窗口类型,可以显示在应用窗口之上WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE//状态栏不接受按键事件//这一标记将使得状态栏接受导致设备唤醒的触摸事件,通常这一事件会在interceptMotionBeforeQueueing()//的过程中被用于唤醒设备(或从变暗状态下恢复),而InputDispatcher会阻止这一事件发送给窗口| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING//允许状态栏支持触摸事件序列的拆分| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,PixelFormat.TRANSLUCENT);//状态栏的Surface像素格式为支持透明度mLp.token = new Binder();mLp.gravity = Gravity.TOP;mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;mLp.setTitle("StatusBar");mLp.packageName = mContext.getPackageName();mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;mStatusBarView = statusBarView;mBarHeight = barHeight;mWindowManager.addView(mStatusBarView, mLp);mLpChanged = new WindowManager.LayoutParams();mLpChanged.copyFrom(mLp);}

当用户按下状态栏导致窗帘下拉时,StatusBarWindowManager通过mWindowManager.updateViewLayout(mStatusBarView, mLp)修改窗口的LayoutParams高度为MATCH_PARENT,即全屏显示,移除WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,可以监听back键收起窗帘

状态栏的高度,资源定义在frameworks\base\core\res\res\values\dimens.xml

1 通过getStatusBarHeight()方法获取状态栏的高度public int getStatusBarHeight() {if (mNaturalBarHeight < 0) {final Resources res = mContext.getResources();mNaturalBarHeight =res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);}return mNaturalBarHeight;}
3 创建状态栏的控件树
protected void makeStatusBarView() {
...
inflateStatusBarWindow(context);
...
FragmentHostManager.get(mStatusBarWindow).addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {CollapsedStatusBarFragment statusBarFragment =(CollapsedStatusBarFragment) fragment;statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);mStatusBarView = (PhoneStatusBarView) fragment.getView();mStatusBarView.setBar(this);mStatusBarView.setPanel(mNotificationPanel);mStatusBarView.setScrimController(mScrimController);mStatusBarView.setBouncerShowing(mBouncerShowing);if (mHeadsUpAppearanceController != null) {// This view is being recreated, let's destroy the old onemHeadsUpAppearanceController.destroy();}mHeadsUpAppearanceController = new HeadsUpAppearanceController(mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);setAreThereNotifications();checkBarModes();}).getFragmentManager().beginTransaction().replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),CollapsedStatusBarFragment.TAG)mit();
}加载了状态栏根布局 super_status_bar.xmlprotected void inflateStatusBarWindow(Context context) {mStatusBarWindow = (StatusBarWindowView) View.inflate(context,R.layout.super_status_bar, null);}

状态栏根布局 StatusBarWindowView,截获ACTION_DOWN的触摸事件后,会修改窗口的高度为MATCH_PARENT,然后将其他跟随触摸,实现卷帘的下拉效果。

顶部状态栏 status_bar_container FrameLayout
包括:notification statusIcon signal 电池 时钟的添加和更新

statusIcon的初始化与更新
系统状态的显示,比如蓝牙、闹铃、定位、省流量开关
frameworks/base/core/res/res/values/config.xml
<string-array name="config_statusBarIcons">ps:frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.javaFragement加载的布局 status_bar.xml@Overridepublic View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,Bundle savedInstanceState) {return inflater.inflate(R.layout.status_bar, container, false);}在CollapsedStatusBarFragment.java onViewCreated中加载statusIcons
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);mStatusBar = (PhoneStatusBarView) view;if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) {mStatusBar.go(savedInstanceState.getInt(EXTRA_PANEL_STATE));}mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons));mDarkIconManager.setShouldLog(true);Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);mClockView = mStatusBar.findViewById(R.id.clock);showSystemIconArea(false);showClock(false);initEmergencyCryptkeeperText();initOperatorName();
}

顶部状态栏Fragement加载的布局 status_bar.xml
布局包括:
status_bar_container FrameLayout 顶部状态栏
status_bar_expanded.xml QuickSettings NotificationExpand
brightness_mirror.xml亮度调节

顶部状态栏:notification statusIcon signal 电池 时钟的添加和更新
顶部状态栏创建fragment、CollapsedStatusBarFragment
其中加载status_bar.xml 自定义组合控件
LinearLayout 横向布局包括
status_bar_cotents->
clock
notification_icon_area 显示的都是notifications,如三方和系统的一些通知
system_icons.xml->
statusIcons显示的一些系统状态,如蓝牙、闹铃
signal_cluster_view.xml显示的是信号相关的view,如wifi,cell信号格 battery 独立图标,电池电量显示、时间显示

	clocknotification_icon_area 显示的都是notifications,如三方和系统的一些通知system_icons.xml-> statusIcons显示的一些系统状态,如蓝牙、闹铃signal_cluster_view.xml显示的是信号相关的view,如wifi,cell信号格		   		battery 独立图标,电池电量显示、时间显示注:
8.0 中顶部状态栏 notification icon 部分逻辑有了很大的改动。对比 7.0,多出了个NotificationInflater。并且,icon 和 expand 的逻辑流程是几乎相同的。
增加了分析 notification icon 相关流程的复杂度

StatusBarWindowView.java(包括 1、顶部状态栏 2、亮度调节 3、QuickSettings Notification Expand)
负责整体框架绘制,点击事件监听等

一说:顶部statusBar初始化、分块、statusIcon块的添加和更新
StatusIcon块,主要负责的是系统状态的显示,比如蓝牙、闹铃、定位、省流量开关等。
这些Icon,都是系统预定好了是哪些。并在一个配置文件定义了slot,或者说是标签。
如果你想加一个新类型图标,首先要修改的是这个文件中的config_statusBarIcons数组

ps:frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java在onViewCreated中,加载了statusIcons布局
87    @Override
88    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
89        super.onViewCreated(view, savedInstanceState);
90        mStatusBar = (PhoneStatusBarView) view;
91        if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) {
92            mStatusBar.go(savedInstanceState.getInt(EXTRA_PANEL_STATE));
93        }一说:顶部statusBar初始化、分块、statusIcon块的添加和更新
94        mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons));
95        mDarkIconManager.setShouldLog(true);
96        Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);二说:内容计划是信号群icon与电池、时钟相关
97        mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
98        mClockView = mStatusBar.findViewById(R.id.clock);
99        showSystemIconArea(false);
100      showClock(false);
101      initEmergencyCryptkeeperText();
102      initOperatorName();
103    }
ps:frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
DarkIconManager为内部类构造方法,StatusBarIconController状态栏图标的控制类public static class DarkIconManager extends IconManager {。。。97       public DarkIconManager(LinearLayout linearLayout) {98            super(linearLayout);99            mIconHPadding = mContext.getResources().getDimensionPixelSize(100                    R.dimen.status_bar_icon_padding);101            mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);102        }。。。}

PhoneStatusBarView.java成为基类,顶部状态栏的view

二说:内容计划是信号群icon与电池、时钟相关
三说:notification icon相关

public interface StatusBarIconController {
...
public void addIconGroup(IconManager iconManager);
}config_statusBarIcons res/values/Strings.xml
实现类 
public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable,ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController {构造方法初始化所有图标
public StatusBarIconControllerImpl(Context context) {super(context.getResources().getStringArray(com.android.internal.R.array.config_statusBarIcons));Dependency.get(ConfigurationController.class).addCallback(this);
...
}
}

StatusBarIconList.java 父类的构造方法public class StatusBarIconList {private ArrayList<Slot> mSlots = new ArrayList<>();mSlots 包含所有图标信息public StatusBarIconList(String[] slots) {final int N = slots.length;for (int i=0; i < N; i++) {mSlots.add(new Slot(slots[i], null));}}
}@Override
public void addIconGroup(IconManager group) {mIconGroups.add(group);List<Slot> allSlots = getSlots();for (int i = 0; i < allSlots.size(); i++) {Slot slot = allSlots.get(i);List<StatusBarIconHolder> holders = slot.getHolderListInViewOrder();boolean blocked = mIconBlacklist.contains(slot.getName());for (StatusBarIconHolder holder : holders) {int tag = holder.getTag();int viewIndex = getViewIndex(getSlotIndex(slot.getName()), holder.getTag());group.onIconAdded(viewIndex, slot.getName(), blocked, holder);}}
}Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
StatusBarIconController内部类
public static class IconManager implements DemoMode {public IconManager(ViewGroup group) {... 图标的尺寸mIconSize = mContext.getResources().getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);...}
}添加图标 根据类型
protected StatusIconDisplayable addHolder(int index, String slot, boolean blocked,StatusBarIconHolder holder) {switch (holder.getType()) {case TYPE_ICON:return addIcon(index, slot, blocked, holder.getIcon());case TYPE_WIFI:return addSignalIcon(index, slot, holder.getWifiState());case TYPE_MOBILE:return addMobileIcon(index, slot, holder.getMobileState());}return null;
}

Dependency.java

 Dependency.start()中初始化很多控制类
public class Dependency extends SystemUI {@Override
public void start() {// TODO: Think about ways to push these creation rules out of Dependency to cut down// on imports.mProviders.put(TIME_TICK_HANDLER, () -> {HandlerThread thread = new HandlerThread("TimeTick");thread.start();return new Handler(thread.getLooper());});mProviders.put(BG_LOOPER, () -> {HandlerThread thread = new HandlerThread("SysUiBg",Process.THREAD_PRIORITY_BACKGROUND);thread.start();return thread.getLooper();});mProviders.put(MAIN_HANDLER, () -> new Handler(Looper.getMainLooper()));mProviders.put(ActivityStarter.class, () -> new ActivityStarterDelegate());mProviders.put(ActivityStarterDelegate.class, () ->getDependency(ActivityStarter.class));mProviders.put(AsyncSensorManager.class, () ->new AsyncSensorManager(mContext.getSystemService(SensorManager.class)));mProviders.put(BluetoothController.class, () ->new BluetoothControllerImpl(mContext, getDependency(BG_LOOPER)));mProviders.put(LocationController.class, () ->new LocationControllerImpl(mContext, getDependency(BG_LOOPER)));
...
}StatusBar.java start()
mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);
在此完成所有StatusIcon的初始化和状态监听,完成图标的隐藏或添加VPN提示、Ethernet图标、Wifi图标、Airplane提示、NoSim提示,移动网络图标等共6类

导航栏

BACK/HOME/RECENT7.3.1 导航栏的创建
//1 是否需要导航栏
boolean showNav = mWindowManagerService.hasNavigationBar();
if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
if (showNav) {createNavigationBar();
}protected void createNavigationBar() {mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> {mNavigationBar = (NavigationBarFragment) fragment;if (mLightBarController != null) {mNavigationBar.setLightBarController(mLightBarController);}mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility);});
}@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,Bundle savedInstanceState) {return inflater.inflate(R.layout.navigation_bar, container, false);
}是否使用导航栏位于 config_showNavigationBar 的取值,有物理按键的设备
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);mNavigationBarView = (NavigationBarView) view;mNavigationBarView.setDisabledFlags(mDisabledFlags1);mNavigationBarView.setComponents(mRecents, mDivider, mStatusBar.getPanel());mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);mNavigationBarView.setOnTouchListener(this::onNavigationTouch);if (savedInstanceState != null) {mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState);}
//1 prepareNavigationBarView()负责为NavigationBarView中的虚拟按键设置用于响应用户触摸事件的监听器prepareNavigationBarView();checkNavBarModes();setDisabled2Flags(mDisabledFlags2);IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);filter.addAction(Intent.ACTION_SCREEN_ON);filter.addAction(Intent.ACTION_USER_SWITCHED);getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);notifyNavigationBarScreenOn();mOverviewProxyService.addCallback(mOverviewProxyListener);
}NavigationBarView根控件定义了2套导航栏的控件树,水平方向@id/rot0,垂直方向@id/rot90public static View create(Context context, FragmentListener listener) {WindowManager.LayoutParams lp = new WindowManager.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH| WindowManager.LayoutParams.FLAG_SLIPPERY,PixelFormat.TRANSLUCENT);lp.token = new Binder();lp.setTitle("NavigationBar");lp.accessibilityTitle = context.getString(R.string.nav_bar);lp.windowAnimations = 0;View navigationBarView = LayoutInflater.from(context).inflate(R.layout.navigation_bar_window, null);if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);if (navigationBarView == null) return null;context.getSystemService(WindowManager.class).addView(navigationBarView, lp);FragmentHostManager fragmentHost = FragmentHostManager.get(navigationBarView);NavigationBarFragment fragment = new NavigationBarFragment();fragmentHost.getFragmentManager().beginTransaction().replace(R.id.navigation_bar_frame, fragment, TAG)mit();fragmentHost.addTagListener(TAG, listener);return navigationBarView;
}横导航栏
navigation_bar_height
竖导航栏
navigation_bar_height_landscape
调整导航栏尺寸,全交给PhoneWindowManager
frameworks/base/core/res/res/values/dimens.xml防误触的死区
res/values/dimens.xml
navigation_bar_deadzone_size7.3.2 虚拟按键的工作原理
1 从触摸事件到按键事件的映射
KeyButtonView将触摸事件转换为按键事件
public boolean onTouchEvent(MotionEvent ev) {final boolean showSwipeUI = mOverviewProxyService.shouldShowSwipeUpUI();final int action = ev.getAction();int x, y;if (action == MotionEvent.ACTION_DOWN) {mGestureAborted = false;}if (mGestureAborted) {setPressed(false);return false;}switch (action) {case MotionEvent.ACTION_DOWN:mDownTime = SystemClock.uptimeMillis();mLongClicked = false;setPressed(true);// Use raw X and Y to detect gestures in case a parent changes the x and y valuesmTouchDownX = (int) ev.getRawX();mTouchDownY = (int) ev.getRawY();if (mCode != 0) {sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);} else {// Provide the same haptic feedback that the system offers for virtual keys.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);}if (!showSwipeUI) {playSoundEffect(SoundEffectConstants.CLICK);}removeCallbacks(mCheckLongPress);postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());break;case MotionEvent.ACTION_MOVE:x = (int)ev.getRawX();y = (int)ev.getRawY();boolean exceededTouchSlopX = Math.abs(x - mTouchDownX) > (mIsVertical? NavigationBarCompat.getQuickScrubTouchSlopPx(): NavigationBarCompat.getQuickStepTouchSlopPx());boolean exceededTouchSlopY = Math.abs(y - mTouchDownY) > (mIsVertical? NavigationBarCompat.getQuickStepTouchSlopPx(): NavigationBarCompat.getQuickScrubTouchSlopPx());if (exceededTouchSlopX || exceededTouchSlopY) {// When quick step is enabled, prevent animating the ripple triggered by// setPressed and decide to run it on touch upsetPressed(false);removeCallbacks(mCheckLongPress);}break;case MotionEvent.ACTION_CANCEL:setPressed(false);if (mCode != 0) {sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);}removeCallbacks(mCheckLongPress);break;case MotionEvent.ACTION_UP:final boolean doIt = isPressed() && !mLongClicked;setPressed(false);final boolean doHapticFeedback = (SystemClock.uptimeMillis() - mDownTime) > 150;if (showSwipeUI) {if (doIt) {// Apply haptic feedback on touch up since there is none on touch downperformHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);playSoundEffect(SoundEffectConstants.CLICK);}} else if (doHapticFeedback && !mLongClicked) {// Always send a release ourselves because it doesn't seem to be sent elsewhere// and it feels weird to sometimes get a release haptic and other times not.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE);}if (mCode != 0) {if (doIt) {sendEvent(KeyEvent.ACTION_UP, 0);sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);} else {sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);}} else {// no key code, just a regular ImageViewif (doIt && mOnClickListener != null) {mOnClickListener.onClick(this);sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);}}removeCallbacks(mCheckLongPress);break;}return true;
}2 按键事件的发送
void sendEvent(int action, int flags, long when) {mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_NAV_BUTTON_EVENT).setType(MetricsEvent.TYPE_ACTION).setSubtype(mCode).addTaggedData(MetricsEvent.FIELD_NAV_ACTION, action).addTaggedData(MetricsEvent.FIELD_FLAGS, flags));final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0;final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount,0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,InputDevice.SOURCE_KEYBOARD);InputManager.getInstance().injectInputEvent(ev,InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}7.3.4 关于导航栏的其他话题
1 菜单键的可见性
PhoneWindow.generateLayout()2 修改BACK的图标
StatusBar.java
setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);3 导航栏方向的选择@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {if (DEBUG) Log.d(TAG, String.format("onSizeChanged: (%dx%d) old: (%dx%d)", w, h, oldw, oldh));//检查NavigationBarView处于竖直状态或水平状态final boolean newVertical = w > 0 && h > w;if (newVertical != mVertical) {mVertical = newVertical;//Log.v(TAG, String.format("onSizeChanged: h=%d, w=%d, vert=%s", h, w, mVertical?"y":"n"));//当状态变化时,会在@id/rot0 @id/rot90 之间选择 reorient();notifyVerticalChangedListener(newVertical);}postCheckForInvalidLayout("sizeChanged");super.onSizeChanged(w, h, oldw, oldh);
}public void reorient() {updateCurrentView();((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone);mDeadZone.onConfigurationChanged(mCurrentRotation);// force the low profile & disabled states into compliancemBarTransitions.init();setMenuVisibility(mShowMenu, true /* force */);if (DEBUG) {Log.d(TAG, "reorient(): rot=" + mCurrentRotation);}// Resolve layout direction if not resolved since components changing layout direction such// as changing languages will recreate this view and the direction will be resolved laterif (!isLayoutDirectionResolved()) {resolveLayoutDirection();}updateTaskSwitchHelper();updateNavButtonIcons();getHomeButton().setVertical(mVertical);
}private void updateCurrentView() {//1 获取屏幕旋转角度0 90 180 270final int rot = mDisplay.getRotation();for (int i=0; i<4; i++) {mRotatedViews[i].setVisibility(View.GONE);}//选择对应方向为当前view,设置为可见mCurrentView = mRotatedViews[rot];mCurrentView.setVisibility(View.VISIBLE);mNavigationInflaterView.setAlternativeOrder(rot == Surface.ROTATION_90);mNavigationInflaterView.updateButtonDispatchersCurrentView();updateLayoutTransitionsEnabled();mCurrentRotation = rot;
}
mRotatedViews数组初始化位于onFinishInflate()@Override
public void onFinishInflate() {mNavigationInflaterView = findViewById(R.id.navigation_inflater);mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers);getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);DockedStackExistsListener.register(mDockedListener);updateRotatedViews();
}private void updateRotatedViews() {
竖直mRotatedViews[Surface.ROTATION_0] =mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
水平mRotatedViews[Surface.ROTATION_270] =mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90);updateCurrentView();
}导航栏选择不同控件树时机在onSizeChanged(),然后reorient()通过mDisplay.getRotation()获取屏幕方向,在mRotatedViews数组中选择2种控件7.3.5 导航栏总结
1 核心工作是将用户的触摸事件转换成相应的按键事件,并发送给InputDispatcher,主要实现者是导航栏内的KeyButtonView
2 PhoneWindowManager对导航栏进行布局7.4 禁用状态栏和导航栏的功能
锁屏下,隐藏虚拟按键,通过密码或图案进行解锁保护,不希望用户拉出下来窗口
7.4.1 如何禁用状态栏和导航栏的功能StatusBarManager.java
public void disable(int what) {try {final IStatusBarService svc = getService();if (svc != null) {svc.disable(what, mToken, mContext.getPackageName());}} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}
}
禁止下拉
public static final int DISABLE_EXPAND = View.STATUS_BAR_DISABLE_EXPAND;
隐藏状态栏中的通知图标
public static final int DISABLE_NOTIFICATION_ICONS = View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS;
禁止通知图标
public static final int DISABLE_NOTIFICATION_ALERTS= View.STATUS_BAR_DISABLE_NOTIFICATION_ALERTS;
禁止Ticker
public static final int DISABLE_NOTIFICATION_TICKER= View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER;
隐藏状态栏中系统状态图标区、信号区、电量、时钟
public static final int DISABLE_SYSTEM_INFO = View.STATUS_BAR_DISABLE_SYSTEM_INFO;
public static final int DISABLE_HOME = View.STATUS_BAR_DISABLE_HOME;
public static final int DISABLE_RECENT = View.STATUS_BAR_DISABLE_RECENT;
public static final int DISABLE_BACK = View.STATUS_BAR_DISABLE_BACK;
public static final int DISABLE_CLOCK = View.STATUS_BAR_DISABLE_CLOCK;
public static final int DISABLE_SEARCH = View.STATUS_BAR_DISABLE_SEARCH;@Deprecated
public static final int DISABLE_NAVIGATION = View.STATUS_BAR_DISABLE_HOME | View.STATUS_BAR_DISABLE_RECENT;public static final int DISABLE_NONE = 0x00000000;使用这个功能需要系统权限 android.permission.STATUS_BAR7.4.2 StatusBarManagerService对禁用标记的维护
@Override
public void disable(int what, IBinder token, String pkg) {disableForUser(what, token, pkg, mCurrentUserId);
}@Override
public void disableForUser(int what, IBinder token, String pkg, int userId) {enforceStatusBar();synchronized (mLock) {disableLocked(userId, what, token, pkg, 1);}
}7.4.3 状态栏与导航栏对禁用标记的响应
7.5 理解SystemUIVisibility
第三方用户使用,提供给他们接口,用于隐藏 显示 导航栏 状态栏

通知栏Notification Panel
QuickSettings和Notification展示系统或应用通知内容。提供快速系统设置开关
Notification流程
初始化通知栏区域
statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);

        FragmentHostManager.get(mStatusBarWindow).addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {CollapsedStatusBarFragment statusBarFragment =(CollapsedStatusBarFragment) fragment;statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);mStatusBarView = (PhoneStatusBarView) fragment.getView();mStatusBarView.setBar(this);mStatusBarView.setPanel(mNotificationPanel);传递 NotificationPanelView 显示下拉UI控制...}

获取到 notification_icon_area,FrameLayout转为ViewGroup,调用 notificationIconAreaController 获取通知要显示的view(LinearLayout),
如果已经有显示的view,通过 view 父布局将其自身remove,然后再重新addView。最后将 mNotificationIconAreaInner 显示出来(设置透明度为1,visibility为VISIBLE)

    public void initNotificationIconArea(NotificationIconAreaControllernotificationIconAreaController) {ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);mNotificationIconAreaInner =notificationIconAreaController.getNotificationInnerAreaView();if (mNotificationIconAreaInner.getParent() != null) {((ViewGroup) mNotificationIconAreaInner.getParent()).removeView(mNotificationIconAreaInner);}notificationIconArea.addView(mNotificationIconAreaInner);// Default to showing until we know otherwise.showNotificationIconArea(false);}

动画隐藏通知栏
当状态栏下拉时,状态栏中的图标icon会慢慢的变成透明和不可见,就是通过hideSystemIconArea(true), hideNotificationIconArea(true)

    public void hideNotificationIconArea(boolean animate) {animateHide(mNotificationIconAreaInner, animate);}public void showNotificationIconArea(boolean animate) {animateShow(mNotificationIconAreaInner, animate);}

getNotificationInnerAreaView()方法中看看通知栏icon对应的容器

    public View getNotificationInnerAreaView() {return mNotificationIconArea;}protected void initializeNotificationAreaViews(Context context) {reloadDimens(context);LayoutInflater layoutInflater = LayoutInflater.from(context);mNotificationIconArea = inflateIconArea(layoutInflater);mNotificationIcons = (NotificationIconContainer) mNotificationIconArea.findViewById(R.id.notificationIcons);mNotificationScrollLayout = mStatusBar.getNotificationScrollLayout();}protected View inflateIconArea(LayoutInflater inflater) {return inflater.inflate(R.layout.notification_icon_area, null);}

notification_icon_area.xml
notification_icon_area(FrameLayout) 中添加 notification_icon_area_inner(LinearLayout),
每一个通知对应的bean为 NotificationData,创建 Notification 添加到 NotificationIconContainer(FrameLayout)中

statusBar 的start()中注册 NotificationListenerWithPlugins 作为系统service监听通知消息

start()->
创建Notification的监听mNotificationListener =  Dependency.get(NotificationListener.class);......mNotificationListener.setUpWithPresenter(this, mEntryManager);public class NotificationListener extends NotificationListenerWithPlugins {public void setUpWithPresenter(NotificationPresenter presenter,NotificationEntryManager entryManager) {mPresenter = presenter;mEntryManager = entryManager;try {registerAsSystemService(mContext,new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),UserHandle.USER_ALL);} catch (RemoteException e) {Log.e(TAG, "Unable to register notification listener", e);}}@Overridepublic void onNotificationRemoved(StatusBarNotification sbn,final RankingMap rankingMap) {if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {final String key = sbn.getKey();mPresenter.getHandler().post(() -> {mEntryManager.removeNotification(key, rankingMap);});}}
ps:NotificationEntryManager.java
@Overridepublic void addNotification(StatusBarNotification notification,NotificationListenerService.RankingMap ranking) {try {addNotificationInternal(notification, ranking);} catch (InflationException e) {handleInflationException(notification, e);}}private void addNotificationInternal(StatusBarNotification notification,NotificationListenerService.RankingMap ranking) throws InflationException {...            NotificationData.Entry shadeEntry = createNotificationViews(notification);}protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)throws InflationException {if (DEBUG) {Log.d(TAG, "createNotificationViews(notification=" + sbn);}NotificationData.Entry entry = new NotificationData.Entry(sbn);Dependency.get(LeakDetector.class).trackInstance(entry);entry.createIcons(mContext, sbn);// Construct the expanded view.inflateViews(entry, mListContainer.getViewParentForNotification(entry));return entry;}private void inflateViews(NotificationData.Entry entry, ViewGroup parent) {PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,entry.notification.getUser().getIdentifier());final StatusBarNotification sbn = entry.notification;if (entry.row != null) {entry.reset();updateNotification(entry, pmUser, sbn, entry.row);} else {new RowInflaterTask().inflate(mContext, parent, entry,row -> {bindRow(entry, pmUser, sbn, row);updateNotification(entry, pmUser, sbn, row);});}}ps:RowInflaterTask.javapublic void inflate(Context context, ViewGroup parent, NotificationData.Entry entry,RowInflationFinishedListener listener) {if (TRACE_ORIGIN) {mInflateOrigin = new Throwable("inflate requested here");}mListener = listener;AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);mEntry = entry;entry.setInflationTask(this);inflater.inflate(R.layout.status_bar_notification_row, parent, this);}
ps:NotificationEntryManager.java@Overridepublic void updateNotification(StatusBarNotification notification,NotificationListenerService.RankingMap ranking) {try {updateNotificationInternal(notification, ranking);} catch (InflationException e) {handleInflationException(notification, e);}}

屏蔽通知栏

ps:frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.javamHandler.post(new EnqueueNotificationRunnable(userId, r));

QuickSettings下拉栏

初始化QuickSettings

        // Set up the quick settings tile panelView container = mStatusBarWindow.findViewById(R.id.qs_frame);if (container != null) {FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,Dependency.get(ExtensionController.class).newExtension(QS.class).withPlugin(QS.class).withFeature(PackageManager.FEATURE_AUTOMOTIVE, CarQSFragment::new).withDefault(QSFragment::new).build());final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,mIconController);mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow,(visible) -> {mBrightnessMirrorVisible = visible;updateScrimController();});fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {QS qs = (QS) f;if (qs instanceof QSFragment) {((QSFragment) qs).setHost(qsh);mQSPanel = ((QSFragment) qs).getQsPanel();mQSPanel.setBrightnessMirror(mBrightnessMirrorController);mKeyguardStatusBar.setQSPanel(mQSPanel);}});}

QSFragment中增加qs布局

    @Overridepublic View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,Bundle savedInstanceState) {inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme));return inflater.inflate(R.layout.qs_panel, container, false);}@Overridepublic void onViewCreated(View view, @Nullable Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);mQSPanel = view.findViewById(R.id.quick_settings_panel);mQSDetail = view.findViewById(R.id.qs_detail);mHeader = view.findViewById(R.id.header);mFooter = view.findViewById(R.id.qs_footer);mContainer = view.findViewById(id.quick_settings_container);mQSDetail.setQsPanel(mQSPanel, mHeader, (View) mFooter);mQSAnimator = new QSAnimator(this,mHeader.findViewById(R.id.quick_qs_panel), mQSPanel);mQSCustomizer = view.findViewById(R.id.qs_customize);mQSCustomizer.setQs(this);if (savedInstanceState != null) {setExpanded(savedInstanceState.getBoolean(EXTRA_EXPANDED));setListening(savedInstanceState.getBoolean(EXTRA_LISTENING));int[] loc = new int[2];View edit = view.findViewById(android.R.id.edit);edit.getLocationInWindow(loc);int x = loc[0] + edit.getWidth() / 2;int y = loc[1] + edit.getHeight() / 2;mQSCustomizer.setEditLocation(x, y);mQSCustomizer.restoreInstanceState(savedInstanceState);}SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);}在Fragment中添加view,QSPanle
1、QS排序
res/values/config.xml
ame="quick_settings_tiles_default" translatable="false">wifi,cell,battery,dnd,flashlight,rotation,bt,airplane </string>2、QS中的行列
public class QSPanel extends LinearLayout implements Tunable, Callback, BrightnessMirrorListener {}@Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();final TunerService tunerService = Dependency.get(TunerService.class);tunerService.addTunable(this, QS_SHOW_BRIGHTNESS);if (mHost != null) {setTiles(mHost.getTiles());}if (mBrightnessMirrorController != null) {mBrightnessMirrorController.addCallback(this);}}public void setTiles(Collection<QSTile> tiles) {setTiles(tiles, false);}public void setTiles(Collection<QSTile> tiles, boolean collapsedView) {if (!collapsedView) {mQsTileRevealController.updateRevealedTiles(tiles);}for (TileRecord record : mRecords) {mTileLayout.removeTile(record);record.tile.removeCallback(record.callback);}mRecords.clear();for (QSTile tile : tiles) {addTile(tile, collapsedView);}}protected TileRecord addTile(final QSTile tile, boolean collapsedView) {final TileRecord r = new TileRecord();r.tile = tile;r.tileView = createTileView(tile, collapsedView);final QSTile.Callback callback = new QSTile.Callback() {...}
}ps:QSTileHost.java
public QSTileView createTileView(QSTile tile, boolean collapsedView) {for (int i = 0; i < mQsFactories.size(); i++) {QSTileView view = mQsFactories.get(i).createTileView(tile, collapsedView);if (view != null) {return view;}}throw new RuntimeException("Default factory didn't create view for " + tile.getTileSpec());}QSTileView.java
下拉图标的自定义控件布局

QSPanel是下拉栏的根 布局,在其中加载PagedTileLayout(ViewPager)

public class PagedTileLayout extends ViewPager implements QSTileLayout {@Overridepublic boolean updateResources() {// Update bottom padding, useful for removing extra space once the panel page indicator is// hidden.setPadding(0, 0, 0,getContext().getResources().getDimensionPixelSize(R.dimen.qs_paged_tile_layout_padding_bottom));boolean changed = false;for (int i = 0; i < mPages.size(); i++) {changed |= mPages.get(i).updateResources();}if (changed) {distributeTiles();}return changed;}设置adapter
private void distributeTiles() {if (DEBUG) Log.d(TAG, "Distributing tiles");final int NP = mPages.size();for (int i = 0; i < NP; i++) {mPages.get(i).removeAllViews();}int index = 0;final int NT = mTiles.size();for (int i = 0; i < NT; i++) {TileRecord tile = mTiles.get(i);if (mPages.get(index).isFull()) {if (++index == mPages.size()) {if (DEBUG) Log.d(TAG, "Adding page for "+ tile.tile.getClass().getSimpleName());mPages.add((TilePage) LayoutInflater.from(getContext()).inflate(R.layout.qs_paged_page, this, false));}}if (DEBUG) Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to "+ index);mPages.get(index).addTile(tile);}if (mNumPages != index + 1) {mNumPages = index + 1;while (mPages.size() > mNumPages) {mPages.remove(mPages.size() - 1);}if (DEBUG) Log.d(TAG, "Size: " + mNumPages);mPageIndicator.setNumPages(mNumPages);setAdapter(mAdapter);mAdapter.notifyDataSetChanged();setCurrentItem(0, false);}}内部类TileLayout 中定义行
public static class TilePage extends TileLayout {private int mMaxRows = 3;  行public TilePage(Context context, AttributeSet attrs) {super(context, attrs);updateResources();}@Overridepublic boolean updateResources() {final int rows = getRows();boolean changed = rows != mMaxRows;if (changed) {mMaxRows = rows;requestLayout();}return super.updateResources() || changed;}TileLayout.java
public boolean updateResources() {final Resources res = mContext.getResources();final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns)); 列mCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side);if (mColumns != columns) {mColumns = columns;requestLayout();return true;}return false;}3、QS中各个Tile实现
public abstract class QSTileImpl<TState extends State> implements QSTile {}

LocationTile

1、首先tile需要继承QSTileImpl 
public class LocationTile extends QSTileImpl<BooleanState> {}2、添加interface 作为Controller类
public interface LocationController extends CallbackController<LocationChangeCallback> {
boolean isLocationActive();boolean isLocationEnabled();boolean setLocationEnabled(boolean enabled);public interface LocationChangeCallback {default void onLocationActiveChanged(boolean active) {}default void onLocationSettingsChanged(boolean locationEnabled) {}}
}3、接口的实现类ControllerImpl,这里一般都是用来接收广播信息来处理的流程
public class LocationControllerImpl extends BroadcastReceiver implements LocationController {}4、在自定义的tile类中首先要在构造函数中加入Controller,然后还需要重写如下这些方法,其中handleUpdateState()方法是核心方法,这里去设置QS的state的各个状态等
public LocationTile(QSHost host) {super(host);mController = Dependency.get(LocationController.class);mKeyguard = Dependency.get(KeyguardMonitor.class);
}QSTileImpl.javapublic abstract TState newTileState();abstract protected void handleClick();abstract protected void handleUpdateState(TState state, Object arg);具体逻辑都在LocationTile.java

增加新的状态栏图标和控制逻辑

增加QS 手电筒和控制逻辑

信号塔刷新流程

public class MobileSignalController extends SignalController<MobileSignalController.MobileState, MobileSignalController.MobileIconGroup> {
内部类监听信号变化
class MobilePhoneStateListener extends PhoneStateListener {public MobilePhoneStateListener(int subId, Looper looper) {super(subId, looper);}@Overridepublic void onSignalStrengthsChanged(SignalStrength signalStrength) {if (DEBUG) {Log.d(mTag, "onSignalStrengthsChanged signalStrength=" + signalStrength +((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel())));}mSignalStrength = signalStrength;updateTelephony();}
}

本文标签: 系统服务