systemui其实结构是比较复杂,里面管理各种服务,导航栏,状态栏,近期列表,下拉菜单,关机界面等,其中以导航栏和状态栏,近期列表用的比较多,也是本博文会重点讲解的内容。从结构上来讲下拉菜单和状态栏都是属于statusbar,结构树上也是属于顶层的super_status_bar.xml(StatusBarWindowView),说这么多还不如直接上图,这样大家看的比较清晰直观
从上图可以比较直观的看出来顶层树是super_status_bar,之后会走两个分支status_bar_container和status_bar_expanded,status_bar_container这个分支主要呈现的是状态栏界面,状态栏细分左边和右边,左边是通知栏,右边是系统功能的状态图标显示,status_bar_expanded这个分支主要呈现的下拉菜单界面,其实下拉菜单中又分快捷图标和短信通知栏,但是这些内容在后续的章节会详细说。本章博文主要讲解status_bar_container这个分支
在前面的博文(Android SystemUI之启动流程)有讲过服务的其中mServices[i].start();StatusBar也是SystemUI的一个服务,所以它的启动入口也是从start()方法开始,包括后面的近期列表服务(Recents)。
public void start() {
//主要是有关notification的服务类获取
mGroupManager = Dependency.get(NotificationGroupManager.class);
mVisualStabilityManager = Dependency.get(VisualStabilityManager.class);
mNotificationLogger = Dependency.get(NotificationLogger.class);
mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
mNotificationListener = Dependency.get(NotificationListener.class);//notification监听信息的service
mGroupManager = Dependency.get(NotificationGroupManager.class);
mNetworkController = Dependency.get(NetworkController.class);
mUserSwitcherController = Dependency.get(UserSwitcherController.class);
mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
mScreenLifecycle.addObserver(mScreenObserver);
mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
mBatteryController = Dependency.get(BatteryController.class);//电池管理控制类。
mAssistManager = Dependency.get(AssistManager.class);
mOverlayManager = IOverlayManager.Stub.asInterface(
ServiceManager.getService(Context.OVERLAY_SERVICE));
mLockscreenUserManager = Dependency.get(NotificationLockscreenUserManager.class);
mGutsManager = Dependency.get(NotificationGutsManager.class);
mMediaManager = Dependency.get(NotificationMediaManager.class);
mEntryManager = Dependency.get(NotificationEntryManager.class);//notification管理类,
mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class);
mAppOpsListener = Dependency.get(AppOpsListener.class);
mAppOpsListener.setUpWithPresenter(this, mEntryManager);
mZenController = Dependency.get(ZenModeController.class);
mKeyguardViewMediator = getComponent(KeyguardViewMediator.class);
mColorExtractor = Dependency.get(SysuiColorExtractor.class);
mColorExtractor.addOnColorsChangedListener(this);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mDisplay = mWindowManager.getDefaultDisplay();
updateDisplaySize();
Resources res = mContext.getResources();
mVibrateOnOpening = mContext.getResources().getBoolean(
R.bool.config_vibrateOnIconAnimation);
mVibratorHelper = Dependency.get(VibratorHelper.class);
mScrimSrcModeEnabled = res.getBoolean(R.bool.config_status_bar_scrim_behind_use_src);
mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);
DateTimeView.setReceiverHandler(Dependency.get(Dependency.TIME_TICK_HANDLER));
putComponent(StatusBar.class, this);
// start old BaseStatusBar.start().
mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
Context.DEVICE_POLICY_SERVICE);
mAccessibilityManager = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
//StatusBarManagerService,这个服务端在framework中 :base\services\core\java\com\android\server\statusbar
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mRecents = getComponent(Recents.class);//近期任务列表
mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
mLockPatternUtils = new LockPatternUtils(mContext);//锁屏工具类。密码锁屏,滑动锁屏都会调用这个工具类来解锁。
mMediaManager.setUpWithPresenter(this, mEntryManager);
// Connect in to the status bar manager service
//统筹systemui与mBarService数据交换,方法调用,app设置全屏显示隐藏任务栏,都是通过mCommandQueue来实现的。
mCommandQueue = getComponent(CommandQueue.class);
mCommandQueue.addCallbacks(this);
/* 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;
*/
//上面的参数都是通过CommandQueue.Callbacks里面的函数来设置的。因此在systemui初始化的时候都是空或者0
int[] switches = new int[9];//一些禁用列表
ArrayList binders = new ArrayList<>();
ArrayList iconSlots = new ArrayList<>();//图标名称
ArrayList icons = new ArrayList<>();//图标
Rect fullscreenStackBounds = new Rect();
Rect dockedStackBounds = new Rect();
try {
//mCommandQueue继承IStatusBar.Stub,mCommandQueue是IStatusBar的BN端,mBarService是BP端
//向IStatusBarServie进行注册,并获取所有保存在IStatusBarService中的信息
mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
fullscreenStackBounds, dockedStackBounds);
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
createAndAddWindows();
// Make sure we always have the most current wallpaper info.
IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
mContext.registerReceiver(mWallpaperChangedReceiver, wallpaperChangedFilter);
mWallpaperChangedReceiver.onReceive(mContext, null);
mLockscreenUserManager.setUpWithPresenter(this, mEntryManager);
//初始化switches 的值
mCommandQueue.disable(switches[0], switches[6], false /* animate */);
//systemui 颜色和可见性
setSystemUiVisibility(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 state
int N = iconSlots.size();
for (int i=0; i < N; i++) {
mCommandQueue.setIcon(iconSlots.get(i), icons.get(i));
}
// Set up the initial notification state.
mNotificationListener.setUpWithPresenter(this, mEntryManager);
if (DEBUG) {
Log.d(TAG, String.format(
"init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
icons.size(),
switches[0],
switches[1],
switches[2],
switches[3]
));
}
setHeadsUpUser(mLockscreenUserManager.getCurrentUserId());
IntentFilter internalFilter = new IntentFilter();
internalFilter.addAction(BANNER_ACTION_CANCEL);
internalFilter.addAction(BANNER_ACTION_SETUP);
mContext.registerReceiver(mBannerActionBroadcastReceiver, internalFilter, PERMISSION_SELF,
null);
//yuanqi add start
IntentFilter wifiStatefilter = new IntentFilter();
wifiStatefilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
mContext.registerReceiver(mWifiStateReceiver, wifiStatefilter);
//yuanqi add end
IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
Context.VR_SERVICE));
try {
vrManager.registerListener(mVrStateCallbacks);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to register VR mode state listener: " + e);
}
IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface(
ServiceManager.getService(Context.WALLPAPER_SERVICE));
try {
wallpaperManager.setInAmbientMode(false /* ambientMode */, false /* animated */);
} catch (RemoteException e) {
// Just pass, nothing critical.
}
// end old BaseStatusBar.start().
// Lastly, call to the icon policy to install/update all the icons.
//设置icon的图标策略类
mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);
//设置icon的信号图标策略类
mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController);
mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
mUnlockMethodCache.addListener(this);
//开启锁屏的相关服务
startKeyguard();
KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);
putComponent(DozeHost.class, mDozeServiceHost);
mScreenPinningRequest = new ScreenPinningRequest(mContext);
mFalsingManager = FalsingManager.getInstance(mContext);
Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(this);
Dependency.get(ConfigurationController.class).addCallback(this);
observerProvision();
}
上述代码比较长,也进一步验证了StatusBar在整个SystemUI的重要性
主要做了以下几件事:
1.获取各种服务为后续工作做准备。
2.建立farmework的联系,把自己注册到StatusBarService中以供其他app使用,这也就是为什么我们能在APP层来控制导航栏和状态栏可见的关键
mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders, fullscreenStackBounds, dockedStackBounds);
3. 创建StatusBar :createAndAddWindows() ,并且做一些初始化值。
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);
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());//把状态栏添加到window上面。
}
createAndAddWindows其实没有做什么事,只是调用了addStatusBarWindow,而addStatusBarWindow又调用了makeStatusBarView,所以我们猜测makeStatusBarView是做具体事情的方法
//创建状态栏
protected void makeStatusBarView() {
//1.初始化资源文件:导航栏高度 状态栏高度 通知面板位置和高度等
final Context context = mContext;
updateDisplaySize(); // populates mDisplayMetrics
updateResources();
updateTheme();
//加载layout文件super_status_bar,mStatusBarWindow
inflateStatusBarWindow(context);
if(StatusBar.SYSTEMUI_START_DEBUG)Log.i(StatusBar.TAG_XIAO," makeStatusBarView mStatusBarWindow getWidth"+mStatusBarWindow.getWidth()
+",getHeight:"+mStatusBarWindow.getHeight());
mStatusBarWindow.setService(this);
mStatusBarWindow.setOnTouchListener(getStatusBarWindowTouchListener());//设置触摸监听,如果子类没有销毁touch,父类使用
// TODO: Deal with the ugliness that comes from having some of the statusbar broken out
// into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);//下拉菜单面板
mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller);//下拉通知信息滚动栏
mZenController.addCallback(this);
mActivityLaunchAnimator = new ActivityLaunchAnimator(mStatusBarWindow,
this,
mNotificationPanel,
mStackScroller);
mGutsManager.setUpWithPresenter(this, mEntryManager, mStackScroller, mCheckSaveListener,
key -> {
try {
mBarService.onNotificationSettingsViewed(key);
} catch (RemoteException e) {
// if we're here we're dead
}
});
mNotificationLogger.setUpWithEntryManager(mEntryManager, mStackScroller);
mNotificationPanel.setStatusBar(this);
mNotificationPanel.setGroupManager(mGroupManager);
mAboveShelfObserver = new AboveShelfObserver(mStackScroller);
mAboveShelfObserver.setListener(mStatusBarWindow.findViewById(
R.id.notification_container_parent));
mKeyguardStatusBar = mStatusBarWindow.findViewById(R.id.keyguard_header);//锁屏界面显示
mNotificationIconAreaController = SystemUIFactory.getInstance()//短信icon控制类
.createNotificationIconAreaController(context, this);
inflateShelf();
mNotificationIconAreaController.setupShelf(mNotificationShelf);
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mNotificationIconAreaController);
FragmentHostManager.get(mStatusBarWindow)
.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
if(StatusBar.SYSTEMUI_START_DEBUG)Log.i(StatusBar.TAG_XIAO,"makeStatusBarView addTagListener fragment:"+fragment+",tag:"+tag);
CollapsedStatusBarFragment statusBarFragment =
(CollapsedStatusBarFragment) fragment;
statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);//加载短信通知的icon
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 one
mHeadsUpAppearanceController.destroy();
}
mHeadsUpAppearanceController = new HeadsUpAppearanceController(
mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);
setAreThereNotifications();
checkBarModes();
/// M: add for plmn display feature @{
attachPlmnPlugin();
///@}
}).getFragmentManager()
.beginTransaction()
.replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
CollapsedStatusBarFragment.TAG)
.commit();
mIconController = Dependency.get(StatusBarIconController.class);
mHeadsUpManager = new HeadsUpManagerPhone(context, mStatusBarWindow, mGroupManager, this,
mVisualStabilityManager);
Dependency.get(ConfigurationController.class).addCallback(mHeadsUpManager);
mHeadsUpManager.addListener(this);
mHeadsUpManager.addListener(mNotificationPanel);
mHeadsUpManager.addListener(mGroupManager);
mHeadsUpManager.addListener(mVisualStabilityManager);
mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
putComponent(HeadsUpManager.class, mHeadsUpManager);
mEntryManager.setUpWithPresenter(this, mStackScroller, this, mHeadsUpManager);
mViewHierarchyManager.setUpWithPresenter(this, mEntryManager, mStackScroller);
if (MULTIUSER_DEBUG) {
mNotificationPanelDebugText = mNotificationPanel.findViewById(R.id.header_debug_info);
mNotificationPanelDebugText.setVisibility(View.VISIBLE);
}
try {
//创建导航栏
boolean showNav = mWindowManagerService.hasNavigationBar();
if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
if (showNav) {
createNavigationBar();
}
} catch (RemoteException ex) {
// no window manager? good luck with that
}
mScreenPinningNotify = new ScreenPinningNotify(mContext);
mStackScroller.setLongPressListener(mEntryManager.getNotificationLongClicker());
mStackScroller.setStatusBar(this);
mStackScroller.setGroupManager(mGroupManager);
mStackScroller.setHeadsUpManager(mHeadsUpManager);
mGroupManager.setOnGroupChangeListener(mStackScroller);
mVisualStabilityManager.setVisibilityLocationProvider(mStackScroller);
inflateEmptyShadeView();
inflateFooterView();
mBackdrop = mStatusBarWindow.findViewById(R.id.backdrop);//paint绘制模式
mBackdropFront = mBackdrop.findViewById(R.id.backdrop_front);
mBackdropBack = mBackdrop.findViewById(R.id.backdrop_back);
if (ENABLE_LOCKSCREEN_WALLPAPER) {
mLockscreenWallpaper = new LockscreenWallpaper(mContext, this, mHandler);
}
//layout :keyguard_bottom_area
mKeyguardIndicationController =
SystemUIFactory.getInstance().createKeyguardIndicationController(mContext,
mStatusBarWindow.findViewById(R.id.keyguard_indication_area),
mNotificationPanel.getLockIcon());
mNotificationPanel.setKeyguardIndicationController(mKeyguardIndicationController);
mAmbientIndicationContainer = mStatusBarWindow.findViewById(
R.id.ambient_indication_container);
// set the initial view visibility
setAreThereNotifications();
// TODO: Find better place for this callback.
mBatteryController.addCallback(new BatteryStateChangeCallback() {
@Override
public void onPowerSaveChanged(boolean isPowerSave) {
mHandler.post(mCheckBarModes);
if (mDozeServiceHost != null) {
mDozeServiceHost.firePowerSaveChanged(isPowerSave);
}
}
@Override
public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
// noop
}
});
mLightBarController = Dependency.get(LightBarController.class);
if (mNavigationBar != null) {
mNavigationBar.setLightBarController(mLightBarController);
}
ScrimView scrimBehind = mStatusBarWindow.findViewById(R.id.scrim_behind);
ScrimView scrimInFront = mStatusBarWindow.findViewById(R.id.scrim_in_front);
mScrimController = SystemUIFactory.getInstance().createScrimController(
scrimBehind, scrimInFront, mLockscreenWallpaper,
(state, alpha, color) -> mLightBarController.setScrimState(state, alpha, color),
scrimsVisible -> {
if (mStatusBarWindowManager != null) {
mStatusBarWindowManager.setScrimsVisibility(scrimsVisible);
}
}, DozeParameters.getInstance(mContext),
mContext.getSystemService(AlarmManager.class));
if (mScrimSrcModeEnabled) {
Runnable runnable = () -> {
boolean asSrc = mBackdrop.getVisibility() != View.VISIBLE;
mScrimController.setDrawBehindAsSrc(asSrc);
mStackScroller.setDrawBackgroundAsSrc(asSrc);
};
mBackdrop.setOnVisibilityChangedRunnable(runnable);
runnable.run();
}
mStackScroller.setScrimController(mScrimController);
mDozeScrimController = new DozeScrimController(mScrimController, context,
DozeParameters.getInstance(context));
// Other icons
mVolumeComponent = getComponent(VolumeComponent.class);
/// M: Support "Operator plugin - Customize Carrier Label for PLMN" @{
SIMHelper.setContext(context);
mStatusBarPlmnPlugin = OpSystemUICustomizationFactoryBase.getOpFactory(context)
.makeStatusBarPlmn(context);
if (supportCustomizeCarrierLabel()) {
mCustomizeCarrierLabel = mStatusBarPlmnPlugin.customizeCarrierLabel(
mNotificationPanel, null);
}
/// M: Support "Operator plugin - Customize Carrier Label for PLMN" @}
mNotificationPanel.setUserSetupComplete(mUserSetup);
if (UserManager.get(mContext).isUserSwitcherEnabled()) {
createUserSwitcher();
}
// Set up the quick settings tile panel
//面板
View 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);
}
});
}
mReportRejectedTouch = mStatusBarWindow.findViewById(R.id.report_rejected_touch);
if (mReportRejectedTouch != null) {
updateReportRejectedTouchVisibility();
mReportRejectedTouch.setOnClickListener(v -> {
Uri session = mFalsingManager.reportRejectedTouch();
if (session == null) { return; }
StringWriter message = new StringWriter();
message.write("Build info: ");
message.write(SystemProperties.get("ro.build.description"));
message.write("\nSerial number: ");
message.write(SystemProperties.get("ro.serialno"));
message.write("\n");
PrintWriter falsingPw = new PrintWriter(message);
FalsingLog.dump(falsingPw);
falsingPw.flush();
startActivityDismissingKeyguard(Intent.createChooser(new Intent(Intent.ACTION_SEND)
.setType("*/*")
.putExtra(Intent.EXTRA_SUBJECT, "Rejected touch report")
.putExtra(Intent.EXTRA_STREAM, session)
.putExtra(Intent.EXTRA_TEXT, message.toString()),
"Share rejected touch report")
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
true /* onlyProvisioned */, true /* dismissShade */);
});
}
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
if (!pm.isScreenOn()) {
mBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
}
mGestureWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
"GestureWakeLock");
mVibrator = mContext.getSystemService(Vibrator.class);
int[] pattern = mContext.getResources().getIntArray(
R.array.config_cameraLaunchGestureVibePattern);
mCameraLaunchGestureVibePattern = new long[pattern.length];
for (int i = 0; i < pattern.length; i++) {
mCameraLaunchGestureVibePattern[i] = pattern[i];
}
// receive broadcasts
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG);
context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
IntentFilter demoFilter = new IntentFilter();
if (DEBUG_MEDIA_FAKE_ARTWORK) {
demoFilter.addAction(ACTION_FAKE_ARTWORK);
}
demoFilter.addAction(ACTION_DEMO);
context.registerReceiverAsUser(mDemoReceiver, UserHandle.ALL, demoFilter,
android.Manifest.permission.DUMP, null);
// listen for USER_SETUP_COMPLETE setting (per-user)
mDeviceProvisionedController.addCallback(mUserSetupObserver);
mUserSetupObserver.onUserSetupChanged();
// disable profiling bars, since they overlap and clutter the output on app windows
ThreadedRenderer.overrideProperty("disableProfileBars", "true");
// Private API call to make the shadows look better for Recents
ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f));
}
上述代码块比较长,里面所包含的信息也比较多,除了状态栏的创建还有下拉菜单的创建。我们现在先讲状态栏的创建。
1.初始化资源文件,获取状态栏高度,加载statusbar设备树super_status_bar :inflateStatusBarWindow(context),这个设备树的根View是StatusBarWindowView,StatusBarWindowView继承FrameLayout。
2.添加CollapsedStatusBarFragment,这个CollapsedStatusBarFragment就是作为加载状态栏的Fragment
FragmentHostManager.get(mStatusBarWindow)
.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
CollapsedStatusBarFragment statusBarFragment =
(CollapsedStatusBarFragment) fragment;
statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);//加载短信通知的icon
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 one
mHeadsUpAppearanceController.destroy();
}
mHeadsUpAppearanceController = new HeadsUpAppearanceController(
mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);
setAreThereNotifications();
checkBarModes();
/// M: add for plmn display feature @{
attachPlmnPlugin();
///@}
}).getFragmentManager()
.beginTransaction()
.replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
CollapsedStatusBarFragment.TAG)
.commit();
到CollapsedStatusBarFragment才算状态栏的开始,那么我们思考一下,状态图标是是怎样被加载到状态栏上,数据又如何获取的,搞明白这两个问题,状态栏的流程基本也就清楚了
我们先来看看CollapsedStatusBarFragment做了什么东西。熟悉Fragment的都清楚Fragment的加载流程onCreateView会加载layout,因此我们在oncreate中找到如下代码
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.status_bar, container, false);
}
status_bar.xml的文件
上面的layout还是蛮多内容,但是我们只看三点:
1.根View :StatusBarWindowView,这个是一个继承FrameLayout的View。所以能比较初步的知道这个子View的界面的加载流程
2.@+id/status_bar_contents 这个LinearLayout,里面包含了@+id/status_bar_left_side ,从名字来看就知道是状态栏左边部分。这个状态栏左边部分包含了时钟@+id/clock和短信通知@+id/notification_icon_area,这个我们在开始的是有说过。
3.@+id/system_icon_area这个也是一个LinearLayout包含了@layout/system_icons,这部分就是状态栏右边部分,里面包含了电池图标和系统状态图标
从上面的代码我们能猜测出来,不管是系统状态图标还是短信通知图标都是动态加载,那我们就需要了解它是如何加载的,数据是如何获取的?
我们先来看看短信通知是的view结构加载过程。
public void initNotificationIconArea(NotificationIconAreaController
notificationIconAreaController) {
ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);//短信通知icon
mNotificationIconAreaInner =
notificationIconAreaController.getNotificationInnerAreaView();
//移除自己,再添加自己
if (mNotificationIconAreaInner.getParent() != null) {
((ViewGroup) mNotificationIconAreaInner.getParent())
.removeView(mNotificationIconAreaInner);
}
notificationIconArea.addView(mNotificationIconAreaInner);
// Default to showing until we know otherwise.
showNotificationIconArea(false);
}
如果熟悉View的添加流程很容易看懂上面代码的含义,先移除notificationIconArea上面的子view,然后再添加mNotificationIconAreaInner。
我们需要搞清的是NotificationIconAreaController这个类是做什么的?
mNotificationIconAreaInner又是一个什么样的view?以及initNotificationIconArea这个方法在哪里被调用。
initNotificationIconArea是在StatusBar中添加CollapsedStatusBarFragment 中被调用:statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
NotificationIconAreaController:这个类主要是用于短信icon的控制类。
mNotificationIconAreaInner是通过加载notification_icon_area而生成的view如下代码:
protected View inflateIconArea(LayoutInflater inflater) {
return inflater.inflate(R.layout.notification_icon_area, null);
}
layout.notification_icon_area 如下代码
看到这边好像我们还没看到短信icon被添加的具体代码,先不急,我们先看看数据是如何获取的。
NotificationListener这个类监听notification,至于为什么这个类能监听到短信变化。是因为它继承了NotificationListenerService
每次系统短信变化的时候会回调onNotificationPosted
public void onNotificationPosted(final StatusBarNotification sbn,
final RankingMap rankingMap) {
if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
mPresenter.getHandler().post(() -> {
processForRemoteInput(sbn.getNotification(), mContext);
String key = sbn.getKey();
mEntryManager.removeKeyKeptForRemoteInput(key);
boolean isUpdate =
mEntryManager.getNotificationData().get(key) != null;
// In case we don't allow child notifications, we ignore children of
// notifications that have a summary, since` we're not going to show them
// anyway. This is true also when the summary is canceled,
// because children are automatically canceled by NoMan in that case.
if (!ENABLE_CHILD_NOTIFICATIONS
&& mPresenter.getGroupManager().isChildInGroupWithSummary(sbn)) {
if (DEBUG) {
Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
}
// Remove existing notification to avoid stale data.
if (isUpdate) {
if (DEBUG) Log.d(TAG, "onNotificationPosted, removeNotification: " + sbn);
mEntryManager.removeNotification(key, rankingMap);
} else {
if (DEBUG) Log.d(TAG, "onNotificationPosted, updateRanking: " + sbn);
mEntryManager.getNotificationData()
.updateRanking(rankingMap);
}
return;
}
if (isUpdate) {
mEntryManager.updateNotification(sbn, rankingMap);
} else {
mEntryManager.addNotification(sbn, rankingMap);
}
});
}
}
短信icon第一次添加都systemui会走NotificationEntryManager.addNotification
public 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 {
String key = notification.getKey();
if (CHATTY) Log.d(TAG, "addNotification key=" + key);
mNotificationData.updateRanking(ranking);
if(StatusBar.SYSTEMUI_START_DEBUG)Log.i(StatusBar.TAG_XIAO,"NotificationEntryManager addNotificationInternal StatusBarNotification:"+ notification.toString());
NotificationData.Entry shadeEntry = createNotificationViews(notification);
boolean isHeadsUped = shouldPeek(shadeEntry);
if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
if (shouldSuppressFullScreenIntent(shadeEntry)) {
if (CHATTY) {
Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + key);
}
} else if (mNotificationData.getImportance(key)
< NotificationManager.IMPORTANCE_HIGH) {
if (CHATTY) {
Log.d(TAG, "No Fullscreen intent: not important enough: "
+ key);
}
} else {
// Stop screensaver if the notification has a fullscreen intent.
// (like an incoming phone call)
SystemServicesProxy.getInstance(mContext).awakenDreamsAsync();
// not immersive & a fullscreen alert should be shown
if (CHATTY)
Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
try {
EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
key);
notification.getNotification().fullScreenIntent.send();
shadeEntry.notifyFullScreenIntentLaunched();
mMetricsLogger.count("note_fullscreen", 1);
} catch (PendingIntent.CanceledException e) {
}
}
}
abortExistingInflation(key);
mForegroundServiceController.addNotification(notification,
mNotificationData.getImportance(key));
mPendingNotifications.put(key, shadeEntry);
mGroupManager.onPendingEntryAdded(shadeEntry);
}
protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)
throws InflationException {
if (CHATTY) {
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;
}
从上面的三个方法来看主要工作是:1.StatusBarNotification 里面的信息转换成NotificationData.Entry,并且创建通知icon的显示View。这个View是StatusBarIconView继承imageView。
2.绑定一些监听,比如移除监听。
3.把新添加的信息保持在mPendingNotifications,这个集合保持了所有显示的通知icon
NotificationEntryManager.updateNotification
public void updateNotification(StatusBarNotification notification,
NotificationListenerService.RankingMap ranking) {
try {
updateNotificationInternal(notification, ranking);
} catch (InflationException e) {
handleInflationException(notification, e);
}
}
updateNotificationInternal里面内容很多,我们就不细看,看跟View显示有关的内容:updateNotifications
public void updateNotifications() {
mNotificationData.filterAndSort();
mPresenter.updateNotificationViews();
}
mPresenter是NotificationPresenter的实例,NotificationPresenter是一个接口,那边我们就需要找到它的实现类是在哪里,它的实现类是StatusBar。
StatusBar.updateNotificationViews
public void updateNotificationViews() {
// The function updateRowStates depends on both of these being non-null, so check them here.
// We may be called before they are set from DeviceProvisionedController's callback.
if (mStackScroller == null || mScrimController == null) return;
// Do not modify the notifications during collapse.
if (isCollapsing()) {
addPostCollapseAction(this::updateNotificationViews);
return;
}
mViewHierarchyManager.updateNotificationViews();
updateSpeedBumpIndex();
updateFooter();
updateEmptyShadeView();
updateQsExpansionEnabled();
// Let's also update the icons
mNotificationIconAreaController.updateNotificationIcons();
}
mNotificationIconAreaController这个实例就是我们前面介绍的icon的管理了。
NotificationIconAreaController.updateNotificationIcons
public void updateNotificationIcons() {
updateStatusBarIcons();
updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons,
NotificationShelf.SHOW_AMBIENT_ICONS, false /* hideDismissed */,
false /* hideRepliedMessages */);
applyNotificationIconsTint();
}
public void updateStatusBarIcons() {
updateIconsForLayout(entry -> entry.icon, mNotificationIcons,
false /* showAmbient */, true /* hideDismissed */, true /* hideRepliedMessages */);
}
private void updateIconsForLayout(Function function,
NotificationIconContainer hostLayout, boolean showAmbient, boolean hideDismissed,
boolean hideRepliedMessages) {
ArrayList toShow = new ArrayList<>(
mNotificationScrollLayout.getChildCount());
//if(StatusBar.SYSTEMUI_START_DEBUG)Log.i(StatusBar.TAG_XIAO,"NotificationIconAreaController updateIconsForLayout hostLayout.getChildCount():"+hostLayout.getChildCount()+", mNotificationScrollLayout.getChildCount():"+ mNotificationScrollLayout.getChildCount());
// Filter out ambient notifications and notification children.
for (int i = 0; i < mNotificationScrollLayout.getChildCount(); i++) {
View view = mNotificationScrollLayout.getChildAt(i);
if (view instanceof ExpandableNotificationRow) {
NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
// if(StatusBar.SYSTEMUI_START_DEBUG)Log.i(StatusBar.TAG_XIAO,"NotificationIconAreaController updateIconsForLayout ent key:"+ent.key+" notification:"+ent.notification);
if (shouldShowNotificationIcon(ent, showAmbient, hideDismissed,
hideRepliedMessages)) {
toShow.add(function.apply(ent));
}
}
}
// In case we are changing the suppression of a group, the replacement shouldn't flicker
// and it should just be replaced instead. We therefore look for notifications that were
// just replaced by the child or vice-versa to suppress this.
ArrayMap> replacingIcons = new ArrayMap<>();
ArrayList toRemove = new ArrayList<>();
for (int i = 0; i < hostLayout.getChildCount(); i++) {
View child = hostLayout.getChildAt(i);
if (!(child instanceof StatusBarIconView)) {
continue;
}
if (!toShow.contains(child)) {
boolean iconWasReplaced = false;
StatusBarIconView removedIcon = (StatusBarIconView) child;
String removedGroupKey = removedIcon.getNotification().getGroupKey();
for (int j = 0; j < toShow.size(); j++) {
StatusBarIconView candidate = toShow.get(j);
if (candidate.getSourceIcon().sameAs((removedIcon.getSourceIcon()))
&& candidate.getNotification().getGroupKey().equals(removedGroupKey)) {
if (!iconWasReplaced) {
iconWasReplaced = true;
} else {
iconWasReplaced = false;
break;
}
}
}
if (iconWasReplaced) {
ArrayList statusBarIcons = replacingIcons.get(removedGroupKey);
if (statusBarIcons == null) {
statusBarIcons = new ArrayList<>();
replacingIcons.put(removedGroupKey, statusBarIcons);
}
statusBarIcons.add(removedIcon.getStatusBarIcon());
}
toRemove.add(removedIcon);
}
}
// removing all duplicates
ArrayList duplicates = new ArrayList<>();
for (String key : replacingIcons.keySet()) {
ArrayList statusBarIcons = replacingIcons.get(key);
if (statusBarIcons.size() != 1) {
duplicates.add(key);
}
}
replacingIcons.removeAll(duplicates);
hostLayout.setReplacingIcons(replacingIcons);
final int toRemoveCount = toRemove.size();
for (int i = 0; i < toRemoveCount; i++) {
hostLayout.removeView(toRemove.get(i));
}
final FrameLayout.LayoutParams params = generateIconLayoutParams();
for (int i = 0; i < toShow.size(); i++) {
StatusBarIconView v = toShow.get(i);
// The view might still be transiently added if it was just removed and added again
hostLayout.removeTransientView(v);
if (v.getParent() == null) {
if (hideDismissed) {
v.setOnDismissListener(mUpdateStatusBarIcons);
}
hostLayout.addView(v, i, params);
}
}
hostLayout.setChangingViewPositions(true);
// Re-sort notification icons
final int childCount = hostLayout.getChildCount();
for (int i = 0; i < childCount; i++) {
View actual = hostLayout.getChildAt(i);
StatusBarIconView expected = toShow.get(i);
if (actual == expected) {
continue;
}
hostLayout.removeView(expected);
hostLayout.addView(expected, i);
}
hostLayout.setChangingViewPositions(false);
hostLayout.setReplacingIcons(null);
}
从上面的代码不难看出updateIconsForLayout含税会把StatusBarIconView添加到NotificationIconContainer这个ViewGroup中,而NotificationIconContainer依据前面的分析是挂载在左边短信通知区域。到现在整个的通知信息加载流程算梳理完成。
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));
}
//layout:system_icons view:StatusIconContainer 是LinearLayout
//DarkIconManager是一个对icon管理类。管理StatusBarIconView
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);//系统icon父类LinearLayout
mClockView = mStatusBar.findViewById(R.id.clock);//时间icon
showSystemIconArea(false);
showClock(false);
initEmergencyCryptkeeperText();
initOperatorName();
}
上述代码中我们需要比较关注的代码是:
mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons))
Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
R.id.statusIcons这个其实是在system_icons.xml里面的StatusIconContainer,这个View是一个LinearLayout。
DarkIconManager是StatusBarIconController的一个内部类,DarkIconManager extends IconManager。IconManager这个类里面做的事情是创建StatusBarIconView,部分代码如下
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;
}
@VisibleForTesting
protected StatusBarIconView addIcon(int index, String slot, boolean blocked,
StatusBarIcon icon) {
StatusBarIconView view = onCreateStatusBarIconView(slot, blocked);
view.set(icon);
mGroup.addView(view, index, onCreateLayoutParams());
return view;
}
上述代码来看也只是把StatusIconContainer保存到IconManager里面,到时候把子View的添加到StatusIconContainer。
是什么时候把子view添加到StatusIconContainer呢?
在StatusBar中有这两行代码
mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);
//设置icon的信号图标策略类
mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController);
从名字我们大概能猜到PhoneStatusBarPolicy是除了信号的状态Icon的所有系统Icon。而信号的icon就由StatusBarSignalPolicy来做初始化添加。
PhoneStatusBarPolicy
//eMBMS status
mIconController.setIcon(mSlotEmbms, R.drawable.stat_sys_embms, null);
mIconController.setIconVisibility(mSlotEmbms, false);
// Alarm clock
mIconController.setIcon(mSlotAlarmClock, R.drawable.stat_sys_alarm, null);
mIconController.setIconVisibility(mSlotAlarmClock, false);
// zen
mIconController.setIcon(mSlotZen, R.drawable.stat_sys_zen_important, null);
mIconController.setIconVisibility(mSlotZen, false);
// volume
mIconController.setIcon(mSlotVolume, R.drawable.stat_sys_ringer_vibrate, null);
mIconController.setIconVisibility(mSlotVolume, false);
updateVolumeZen();
// cast
mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast, null);
mIconController.setIconVisibility(mSlotCast, false);
// hotspot
mIconController.setIcon(mSlotHotspot, R.drawable.stat_sys_hotspot,
mContext.getString(R.string.accessibility_status_bar_hotspot));
mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled());
// managed profile
mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status,
mContext.getString(R.string.accessibility_managed_profile));
mIconController.setIconVisibility(mSlotManagedProfile, mManagedProfileIconVisible);
// data saver
mIconController.setIcon(mSlotDataSaver, R.drawable.stat_sys_data_saver,
context.getString(R.string.accessibility_data_saver_on));
mIconController.setIconVisibility(mSlotDataSaver, false);
StatusBarIconControllerImpl
public void setIcon(String slot, int resourceId, CharSequence contentDescription) {
int index = getSlotIndex(slot);
StatusBarIconHolder holder = getIcon(index, 0);
if(StatusBar.SYSTEMUI_START_DEBUG)Log.i(StatusBar.TAG_XIAO,"StatusBarIconControllerImpl setIcon slot:"+slot+",index:"+index+",holder:"+holder);
if (holder == null) {
StatusBarIcon icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
Icon.createWithResource(
mContext, resourceId), 0, 0, contentDescription);
holder = StatusBarIconHolder.fromIcon(icon);
setIcon(index, holder);
} else {
holder.getIcon().icon = Icon.createWithResource(mContext, resourceId);
holder.getIcon().contentDescription = contentDescription;
handleSet(index, holder);
}
}
public void setIcon(int index, @NonNull StatusBarIconHolder holder) {
boolean isNew = getIcon(index, holder.getTag()) == null;
super.setIcon(index, holder);
if (isNew) {
addSystemIcon(index, holder);
} else {
handleSet(index, holder);
}
}
private void addSystemIcon(int index, StatusBarIconHolder holder) {
String slot = getSlotName(index);
int viewIndex = getViewIndex(index, holder.getTag());
boolean blocked = mIconBlacklist.contains(slot);
mIconLogger.onIconVisibility(getSlotName(index), holder.isVisible());
mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, holder));
}
protected void onIconAdded(int index, String slot, boolean blocked,
StatusBarIconHolder holder) {
StatusIconDisplayable view = addHolder(index, slot, blocked, holder);
mDarkIconDispatcher.addDarkReceiver((DarkReceiver) view);
}
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;
}
@VisibleForTesting
protected StatusBarIconView addIcon(int index, String slot, boolean blocked,
StatusBarIcon icon) {
StatusBarIconView view = onCreateStatusBarIconView(slot, blocked);
view.set(icon);
mGroup.addView(view, index, onCreateLayoutParams());
return view;
}
从上面的代码不难看出最后会调用addIcon,创建一个StatusBarIconView 添加到mGroup,而mGroup就是我们在所提到的R.id.statusIcons,也就是StatusIconContainer。到现在系统icon就添加到状态栏右边了。
状态栏流程的研究到此为止