SystemUI 有哪内容
从表面上看, 我们看到的状态栏、通知栏、下拉菜单、导航栏、锁屏、最近任务、低电提示等系统页面都是 SystemUI 的。SystemUI,在源码目录中位于: framework/base/packages 目录下, 可见 SystemUI 和 framework 是关联的, SystemUI 依赖了很多内部 API , 系统资源, SystemUI 编译是要依赖系统源码的。
SystemUI 也是一个应用,不过这个应用特殊之处在于他没有启动图标、也没有入口 Activity 。他的入口程序是一个服务:SystemUIService。 这个服务会被系统服务拉起来, 这个服务起来, SystemUI 应用进程就创建起来了,具体启动过程后面会分析。除了 SystemUIService , SystemUI 还有很多服务, 例如: 负责锁屏的KeyguardService、负责最近任务的 RecentsSystemUserService、负责壁纸的 ImageWallpaper 、负责截屏的TakeScreenshotService 等。
下面是PhoneStatusBarView的view 树形图:
PanelHolder
PanelHolder是用户下拉 status bar 后得到的 view。它主要包含 QuickSettings 和 Notification panel 两个部分。PanelHolder是一个继承自FrameLayout的自定义view,它的内容是通过include status_bar_expanded.xml进行填充的。PanelHolder的布局比较复杂,为了提高view的重用性大量的使用了include标签。下面是PanelHolder的view树形图, 只给出了了主要的view:
架构关系
在系统服务中,有一个服务是专门为 SystemUI 的状态栏服务的, 这个服务就是 StatusbarManagerService (简称:SMS),和这个服务关系比较密切的服务是 WindowManagerService(简称:WMS), SMS 主要管控的是状态栏、导航栏, 例如:我们可以设置全屏、沉浸式状态栏都是 SMS 在起作用。
services组件启动时配置列表 : (R.array.config_systemUIServiceComponents)
所有 SystemUIService 都是继承自 SystemUI.class , SystemUI.class 是一个抽象类
<item>com.android.systemui.util.NotificationChannels</item> 通知信息 <item>com.android.systemui.keyguard.KeyguardViewMediator</item> 锁屏 <item>com.android.systemui.recents.Recents</item> 近期列表 Android 10之后近期列表的显示被移到Launcher里面了。在Launcher3的一个 类中TouchInteractionService.java IBinder mMyBinder = new IOverviewProxy.Stub() 通过AIDL的方法与systemUI通信 ———————————————— <item>com.android.systemui.volume.VolumeUI</item> 声音UI显示 <item>com.android.systemui.statusbar.phone.StatusBar</item> 状态栏及下拉面板 <item>com.android.systemui.usb.StorageNotification</item> usb通知管理 <item>com.android.systemui.power.PowerUI</item> 电源UI显示管理 <item>com.android.systemui.media.RingtonePlayer</item> 播放铃声 <item>com.android.systemui.keyboard.KeyboardUI</item>键盘UI <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>快捷方式 <item>@string/config_systemUIVendorServiceComponent</item>厂商相关定制 <item>com.android.systemui.util.leak.GarbageMonitor$Service</item>垃圾监测器 <item>com.android.systemui.LatencyTester</item> 延迟测试仪 <item>com.android.systemui.globalactions.GlobalActionsComponent</item> 关机界面的显示、全局控制 <item>com.android.systemui.ScreenDecorations</item>屏幕装饰 <item>com.android.systemui.biometrics.AuthController</item>生物识别 <item>com.android.systemui.SliceBroadcastRelayHandler</item> 切片广播 <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item> <item>com.android.systemui.theme.ThemeOverlayController</item> <item>com.android.systemui.accessibility.WindowMagnification</item> <item>com.android.systemui.accessibility.SystemActions</item> <item>com.android.systemui.toast.ToastUI</item> Toast <item>com.android.systemui.wmshell.WMShell</item>
一、SystemUI的启动流程
1、SystemServer
SystemServer启动后,会在Main Thread启动ActivityManagerService,当ActivityManagerService systemReady后,会去启动SystemUIService。
/frameworks/base/services/java/com/android/server/SystemServer.java
①main
/** * The main entry point from zygote. */ public static void main(String[] args) { new SystemServer().run(); }
②run
private void run() { t.traceBegin("InitBeforeStartServices"); .... // Create the system service manager. mSystemServiceManager = new SystemServiceManager(mSystemContext); mSystemServiceManager.setStartInfo(mRuntimeRestart, mRuntimeStartElapsedTime, mRuntimeStartUptime); LocalServices.addService(SystemServiceManager.class, mSystemServiceManager); .... }
③mActivityManagerService.systemReady
mActivityManagerService.systemReady(() -> { //准备好服务 Slog.i(TAG, "Making services ready"); .... //跟踪开启系统界面 t.traceBegin("StartSystemUI"); try { //开启系统界面 startSystemUi(context, windowManagerF); } catch (Throwable e) { reportWtf("starting System UI", e); } t.traceEnd(); .... }
④startSystemUi
private static void startSystemUi(Context context, WindowManagerService windowManager) { PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class); Intent intent = new Intent(); intent.setComponent(pm.getSystemUiServiceComponent()); intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING); //Slog.d(TAG, "Starting service: " + intent); //通过startServiceAsUser,SystemUIService就启动了,即SystemUI进程开机启动 context.startServiceAsUser(intent, UserHandle.SYSTEM); windowManager.onSystemUiStarted(); }
2、systemUIService
在SystemUIService的onCreate方法中会调用SystemUIApplication的startServicesIfNeeded方法,这个方法会调用 startServicesIfNeeded(SERVICES)方法启动一系列服务
@Override public void onCreate() { super.onCreate(); // Start all of SystemUI ((SystemUIApplication) getApplication()).startServicesIfNeeded();
3、SystemUIApplication
/frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
①startServicesIfNeeded()
public void startServicesIfNeeded() { //获取所有的服务的路径,所有SERVICES统一继承了SystemUI类: String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources()); startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names); }
②startServicesIfNeeded(String metricsPrefix, String[] services)
在重载方法中将每一个名称通过反射来得到实例对象,然后依次调用每一个SystemUI的子类的start方法启动每一个模块。
private void startServicesIfNeeded(String metricsPrefix, String[] services) { if (mServicesStarted) { return; } mServices = new SystemUI[services.length]; //检查一下,也许在我们开始之前很久它就已经完成了 if (!mBootCompleteCache.isBootComplete()) { // check to see if maybe it was already completed long before we began // see ActivityManagerService.finishBooting() if ("1".equals(SystemProperties.get("sys.boot_completed"))) { mBootCompleteCache.setBootComplete(); if (DEBUG) { Log.v(TAG, "BOOT_COMPLETED was already sent"); } } } final DumpManager dumpManager = mRootComponent.createDumpManager(); Log.v(TAG, "Starting SystemUI services for user " + Process.myUserHandle().getIdentifier() + "."); TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming", Trace.TRACE_TAG_APP); //开始追踪 log.traceBegin(metricsPrefix); final int N = services.length; //遍历services这个数组 for (int i = 0; i < N; i++) { String clsName = services[i]; if (DEBUG) Log.d(TAG, "loading: " + clsName); log.traceBegin(metricsPrefix + clsName); long ti = System.currentTimeMillis(); try { SystemUI obj = mComponentHelper.resolveSystemUI(clsName); if (obj == null) { Constructor constructor = Class.forName(clsName).getConstructor(Context.class); obj = (SystemUI) constructor.newInstance(this); } mServices[i] = obj; } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException ex) { throw new RuntimeException(ex); } if (DEBUG) Log.d(TAG, "running: " + mServices[i]); //依次调用service的start方法启动服务 mServices[i].start(); log.traceEnd(); // Warn if initialization of component takes too long //如果组件初始化时间过长,则发出警告 ti = System.currentTimeMillis() - ti; if (ti > 1000) { Log.w(TAG, "Initialization of " + clsName + " took " + ti + " ms"); } if (mBootCompleteCache.isBootComplete()) { mServices[i].onBootCompleted(); } dumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]); } mRootComponent.getInitController().executePostInitTasks(); //结束追踪 log.traceEnd(); mServicesStarted = true; }
4、SystemUI
/** * @see SystemUIApplication#startServicesIfNeeded() *系统界面应用 如果需要,启动服务 */ public abstract class SystemUI implements Dumpable { protected final Context mContext; public SystemUI(Context context) { mContext = context; } public abstract void start(); protected void onConfigurationChanged(Configuration newConfig) { } @Override public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { } protected void onBootCompleted() { } public static void overrideNotificationAppName(Context context, Notification.Builder n, boolean system) { final Bundle extras = new Bundle(); String appName = system ? context.getString(com.android.internal.R.string.notification_app_name_system) : context.getString(com.android.internal.R.string.notification_app_name_settings); extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, appName); n.addExtras(extras); } }
二、状态栏
1、SystemBars
SystemBars加载基本全部SystemUI的界面显示,由前面可知调用的start方法实际上是每一个继承于SytemUI的子类中的方法
①start
②createStatusBarFromConfig
从string资源文件里面读取class name,通过java的反射机制实例化对象,然后调用start()方法启动,class name的值如下图:
private void createStatusBarFromConfig() { ...... String clsName = mContext.getString(R.string.config_statusBarComponent); ...... try { cls = mContext.getClassLoader().loadClass(clsName); } catch (Throwable t) { throw andLog("Error loading status bar component: " + clsName, t); } try { mStatusBar = (BaseStatusBar) cls.newInstance(); } catch (Throwable t) { throw andLog("Error creating status bar component: " + clsName, t); } ...... mStatusBar.start(); ...... }
③String
<!-- Component to be used as the status bar service. Must implement the IStatusBar interface. This name is in the ComponentName flattened format (package/class) --> <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string>
2、StatusBar
①createAndAddWindows
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) { //创建状态栏 makeStatusBarView(result); mNotificationShadeWindowController.attach(); //创建状态栏的窗口 mStatusBarWindowController.attach(); }
②makeStatusBarView
protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) { final Context context = mContext; ..... FragmentHostManager.get(mPhoneStatusBarWindow) .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> { //CollapsedStatusBarFragment 替换 status_bar_container(状态栏通知显示区域) CollapsedStatusBarFragment statusBarFragment = (CollapsedStatusBarFragment) fragment; PhoneStatusBarView oldStatusBarView = mStatusBarView; mStatusBarView = (PhoneStatusBarView) statusBarFragment.getView(); //传递statusBar处理下拉事件 mStatusBarView.setBar(this); //传递 NotificationPanelView 显示下拉UI控制 mStatusBarView.setPanel(mNotificationPanelViewController); mStatusBarView.setScrimController(mScrimController); //初始化通知栏区域 statusBarFragment.initNotificationIconArea(mNotificationIconAreaController); ...... }).getFragmentManager() .beginTransaction() .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(), CollapsedStatusBarFragment.TAG) .commit(); .....
2、CollapsedStatusBarFragment
①onCreateView
@Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.status_bar, container, false); }
②initNotificationIconArea
public void initNotificationIconArea(NotificationIconAreaController notificationIconAreaController) { //notification_icon_area是在status_bar.xml的布局,它是属于通知Notification //获取到 notification_icon_area,FrameLayout转为ViewGroup, ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area); //调用 notificationIconAreaController 获取通知要显示的view(LinearLayout) //在4中跟进 mNotificationIconAreaInner = notificationIconAreaController.getNotificationInnerAreaView(); //如果已经有显示的view,通过 view 父布局将其自身remove,然后再重新addView。 //最后将 mNotificationIconAreaInner 显示出来(设置透明度为1,visibility为VISIBLE) if (mNotificationIconAreaInner.getParent() != null) { ((ViewGroup) mNotificationIconAreaInner.getParent()) .removeView(mNotificationIconAreaInner); } notificationIconArea.addView(mNotificationIconAreaInner); //与上面一样 ViewGroup statusBarCenteredIconArea = mStatusBar.findViewById(R.id.centered_icon_area); mCenteredIconArea = notificationIconAreaController.getCenteredNotificationAreaView(); if (mCenteredIconArea.getParent() != null) { ((ViewGroup) mCenteredIconArea.getParent()) .removeView(mCenteredIconArea); } statusBarCenteredIconArea.addView(mCenteredIconArea); //默认为显示,直到我们知道其他情况 // Default to showing until we know otherwise. showNotificationIconArea(false); }
③showNotificationIconArea
当状态栏下拉时,状态栏中的图标icon会慢慢的变成透明和不可见,就是通过hideSystemIconArea(true), hideNotificationIconArea(true)
//当状态栏下拉时,设置状态栏中的图标icon会慢慢的变成透明和不可见 public void hideNotificationIconArea(boolean animate) { animateHide(mNotificationIconAreaInner, animate); animateHide(mCenteredIconArea, animate); } //设置状态栏图标透明度为1,visibility为VISIBLE public void showNotificationIconArea(boolean animate) { animateShow(mNotificationIconAreaInner, animate); animateShow(mCenteredIconArea, animate); } public void hideOperatorName(boolean animate) { if (mOperatorNameFrame != null) { animateHide(mOperatorNameFrame, animate); } } public void showOperatorName(boolean animate) { if (mOperatorNameFrame != null) { animateShow(mOperatorNameFrame, animate); }
④animateShow
private void animateShow(View v, boolean animate) { v.animate().cancel(); //(设置透明度为1,visibility为VISIBLE) v.setVisibility(View.VISIBLE); if (!animate) { v.setAlpha(1f); return; } ..... }
⑤animateHiddenState
//将视图动画化为 INVISIBLE 或 GONE private void animateHiddenState(final View v, int state, boolean animate) { v.animate().cancel(); if (!animate) { v.setAlpha(0f); v.setVisibility(state); return; } v.animate() .alpha(0f) .setDuration(160) .setStartDelay(0) .setInterpolator(Interpolators.ALPHA_OUT) .withEndAction(() -> v.setVisibility(state)); }
3、status_bar.xml
<?xml version="1.0" encoding="utf-8"?> <!-- ** ** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ --> <!-- android:background="@drawable/status_bar_closed_default_background" --> <com.android.systemui.statusbar.phone.PhoneStatusBarView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" android:layout_width="match_parent" android:layout_height="@dimen/status_bar_height" android:id="@+id/status_bar" android:orientation="vertical" android:focusable="false" android:descendantFocusability="afterDescendants" android:accessibilityPaneTitle="@string/status_bar" > <!-- add for KGDAANWIKFRA-135 --> <View android:layout_width="match_parent" android:layout_height="@dimen/status_bar_height" android:id="@+id/status_bar_dark_view" android:background="#ff000000" android:visibility="gone" /> //<!--通知灯,默认gone--> <ImageView android:id="@+id/notification_lights_out" android:layout_width="@dimen/status_bar_icon_size" android:layout_height="match_parent" android:paddingStart="@dimen/status_bar_padding_start" android:paddingBottom="2dip" android:src="@drawable/ic_sysbar_lights_out_dot_small" android:scaleType="center" android:visibility="gone" /> //<!--状态栏内容--> <LinearLayout android:id="@+id/status_bar_contents" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingStart="@dimen/status_bar_padding_start" android:paddingEnd="@dimen/status_bar_padding_end" android:paddingTop="@dimen/status_bar_padding_top" android:orientation="horizontal" > <FrameLayout android:layout_height="match_parent" android:layout_width="0dp" android:layout_weight="1"> <include layout="@layout/heads_up_status_bar_layout" /> <!-- The alpha of the left side is controlled by PhoneStatusBarTransitions, and the individual views are controlled by StatusBarManager disable flags DISABLE_CLOCK and DISABLE_NOTIFICATION_ICONS, respectively --> <LinearLayout android:id="@+id/status_bar_left_side" android:layout_height="match_parent" android:layout_width="match_parent" android:clipChildren="false" > <ViewStub android:id="@+id/operator_name" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout="@layout/operator_name" /> <com.android.systemui.statusbar.policy.Clock android:id="@+id/clock" android:layout_width="wrap_content" android:layout_height="match_parent" android:textAppearance="@style/TextAppearance.StatusBar.Clock" android:singleLine="true" android:paddingStart="@dimen/status_bar_left_clock_starting_padding" android:paddingEnd="@dimen/status_bar_left_clock_end_padding" android:gravity="center_vertical|start" /> //<!--通知图标区域--> <com.android.systemui.statusbar.AlphaOptimizedFrameLayout android:id="@+id/notification_icon_area" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:orientation="horizontal" android:clipChildren="false"/> </LinearLayout> </FrameLayout> <!-- Space should cover the notch (if it exists) and let other views lay out around it --> <android.widget.Space android:id="@+id/cutout_space_view" android:layout_width="0dp" android:layout_height="match_parent" android:gravity="center_horizontal|center_vertical" /> //居中的图标区域 <com.android.systemui.statusbar.AlphaOptimizedFrameLayout android:id="@+id/centered_icon_area" android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="horizontal" android:clipChildren="false" android:gravity="center_horizontal|center_vertical"/> <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:orientation="horizontal" android:gravity="center_vertical|end" > //<!--系统图标--> <include layout="@layout/system_icons" /> </com.android.keyguard.AlphaOptimizedLinearLayout> </LinearLayout> //<!--紧急密码管理员文本--> <ViewStub android:id="@+id/emergency_cryptkeeper_text" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout="@layout/emergency_cryptkeeper_text" /> </com.android.systemui.statusbar.phone.PhoneStatusBarView>
4、NotificationIconAreaController
①getNotificationInnerAreaView
/** * Returns the view that represents the notification area.+ * 返回表示通知区域的视图。 */ public View getNotificationInnerAreaView() { return mNotificationIconArea; }
②initializeNotificationAreaViews
/** * Initializes the views that will represent the notification area. * 初始化将表示通知区域的视图。 */ protected void initializeNotificationAreaViews(Context context) { reloadDimens(context); LayoutInflater layoutInflater = LayoutInflater.from(context); //通知图标区域布局 mNotificationIconArea = inflateIconArea(layoutInflater); //通知图标 mNotificationIcons = mNotificationIconArea.findViewById(R.id.notificationIcons); //获取通知滚动布局 mNotificationScrollLayout = mStatusBar.getNotificationScrollLayout(); //中心图标区域布局 mCenteredIconArea = layoutInflater.inflate(R.layout.center_icon_area, null); //居中的图标 mCenteredIcon = mCenteredIconArea.findViewById(R.id.centeredIcon); initAodIcons(); }
③inflateIconArea
protected View inflateIconArea(LayoutInflater inflater) { return inflater.inflate(R.layout.notification_icon_area, null); }
三、status icon加载流程
1、 status_bar.xml
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout android:id="@+id/centered_icon_area" android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="horizontal" android:clipChildren="false" android:gravity="center_horizontal|center_vertical"/> <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:orientation="horizontal" android:gravity="center_vertical|end" > <!--系统图标--> <include layout="@layout/system_icons" /> </com.android.keyguard.AlphaOptimizedLinearLayout>
2、system_icons.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:id="@+id/system_icons" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_vertical"> //StatusIconContainer继承AlphaOptimizedLinearLayout <com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:paddingEnd="@dimen/signal_cluster_battery_padding" android:gravity="center_vertical" android:orientation="horizontal"/> <com.android.systemui.BatteryMeterView android:id="@+id/battery" android:layout_height="match_parent" android:layout_width="wrap_content" android:clipToPadding="false" android:clipChildren="false" systemui:textAppearance="@style/TextAppearance.StatusBar.Clock" /> </LinearLayout>
3、AlphaOptimizedLinearLayout
//该方法用来标记当前view是否存在过度绘制,存在返回ture,不存在返回false, //api里面默认返回为true,status icon不存在过度绘制。 @Override public boolean hasOverlappingRendering() { return false; }
4、CollapsedStatusBarFragment
@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.restoreHierarchyState( savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE)); } // TINNO BEGIN, add for KGDAANWIKFRA-135 if (Utils.TINNO_NOTCH_SCREEN) { mStatusBarDarkView = mStatusBar.findViewById(R.id.status_bar_dark_view); toggleStatusBarDarkView(); mLastOrientation = getContext().getResources().getConfiguration().orientation; } // TINNO END //DarkIconManager初始化 mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons), Dependency.get(CommandQueue.class)); 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(); }
4、StatusBarIconController
①DarkIconManager
/** * Version of ViewGroup that observes state from the DarkIconDispatcher. */ public static class DarkIconManager extends IconManager { private final DarkIconDispatcher mDarkIconDispatcher; private int mIconHPadding; public DarkIconManager(LinearLayout linearLayout, CommandQueue commandQueue) { super(linearLayout, commandQueue); mIconHPadding = mContext.getResources().getDimensionPixelSize( R.dimen.status_bar_icon_padding); mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class); } //每个icon应该就是对应着代表顺序的index和数据类型为String的slot @Override protected void onIconAdded(int index, String slot, boolean blocked, StatusBarIconHolder holder) { StatusIconDisplayable view = addHolder(index, slot, blocked, holder); mDarkIconDispatcher.addDarkReceiver((DarkReceiver) view); } ..... //onSetIcon可能就是刷新icon状态的 @Override public void onSetIcon(int viewIndex, StatusBarIcon icon) { super.onSetIcon(viewIndex, icon); mDarkIconDispatcher.applyDark((DarkReceiver) mGroup.getChildAt(viewIndex)); } ..... }
②IconManager
public static class IconManager implements DemoMode { ..... protected void onIconAdded(int index, String slot, boolean blocked, StatusBarIconHolder holder) { addHolder(index, slot, blocked, holder); } 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; } ..... }
5、StatusBarIconControllerImpl
//继承StatusBarIconList public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable, ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController { ...... @Inject public StatusBarIconControllerImpl(Context context, CommandQueue commandQueue) { //config_statusBarIcons super(context.getResources().getStringArray( com.android.internal.R.array.config_statusBarIcons)); Dependency.get(ConfigurationController.class).addCallback(this); ..... } }
6、StatusBarIconList
在初始化的时候就已经定义好了所有的slots,然后从framework中加载出来,index就是string-array中的顺序。
public class StatusBarIconList { private ArrayList<Slot> mSlots = new ArrayList<>(); public StatusBarIconList(String[] slots) { final int N = slots.length; for (int i=0; i < N; i++) { mSlots.add(new Slot(slots[i], null)); } }
7、config_statusBarIcons
<string-array name="config_statusBarIcons"> <item><xliff:g id="id">@string/status_bar_rotate</xliff:g></item> <item><xliff:g id="id">@string/status_bar_headset</xliff:g></item> <item><xliff:g id="id">@string/status_bar_data_saver</xliff:g></item> <item><xliff:g id="id">@string/status_bar_managed_profile</xliff:g></item> <item><xliff:g id="id">@string/status_bar_ime</xliff:g></item> <item><xliff:g id="id">@string/status_bar_sync_failing</xliff:g></item> <item><xliff:g id="id">@string/status_bar_sync_active</xliff:g></item> <item><xliff:g id="id">@string/status_bar_cast</xliff:g></item> <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item> <item><xliff:g id="id">@string/status_bar_location</xliff:g></item> <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item> <item><xliff:g id="id">@string/status_bar_nfc</xliff:g></item> <item><xliff:g id="id">@string/status_bar_tty</xliff:g></item> <item><xliff:g id="id">@string/status_bar_speakerphone</xliff:g></item> <item><xliff:g id="id">@string/status_bar_zen</xliff:g></item> <item><xliff:g id="id">@string/status_bar_mute</xliff:g></item> <item><xliff:g id="id">@string/status_bar_volume</xliff:g></item> <item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item> <item><xliff:g id="id">@string/status_bar_ethernet</xliff:g></item> <item><xliff:g id="id">@string/status_bar_wifi</xliff:g></item> <item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item> <item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item> <item><xliff:g id="id">@string/status_bar_cdma_eri</xliff:g></item> <item><xliff:g id="id">@string/status_bar_data_connection</xliff:g></item> <item><xliff:g id="id">@string/status_bar_phone_evdo_signal</xliff:g></item> <item><xliff:g id="id">@string/status_bar_phone_signal</xliff:g></item> <item><xliff:g id="id">@string/status_bar_battery</xliff:g></item> <item><xliff:g id="id">@string/status_bar_alarm_clock</xliff:g></item> <item><xliff:g id="id">@string/status_bar_secure</xliff:g></item> <item><xliff:g id="id">@string/status_bar_clock</xliff:g></item> </string-array> <string translatable="false" name="status_bar_rotate">rotate</string> <string translatable="false" name="status_bar_headset">headset</string> <string translatable="false" name="status_bar_data_saver">data_saver</string> <string translatable="false" name="status_bar_managed_profile">managed_profile</string> <string translatable="false" name="status_bar_ime">ime</string> <string translatable="false" name="status_bar_sync_failing">sync_failing</string> <string translatable="false" name="status_bar_sync_active">sync_active</string> <string translatable="false" name="status_bar_cast">cast</string> <string translatable="false" name="status_bar_hotspot">hotspot</string> <string translatable="false" name="status_bar_location">location</string> <string translatable="false" name="status_bar_bluetooth">bluetooth</string> <string translatable="false" name="status_bar_nfc">nfc</string> <string translatable="false" name="status_bar_tty">tty</string> <string translatable="false" name="status_bar_speakerphone">speakerphone</string> <string translatable="false" name="status_bar_zen">zen</string> <string translatable="false" name="status_bar_mute">mute</string> <string translatable="false" name="status_bar_volume">volume</string> <string translatable="false" name="status_bar_wifi">wifi</string> <string translatable="false" name="status_bar_cdma_eri">cdma_eri</string> <string translatable="false" name="status_bar_data_connection">data_connection</string> <string translatable="false" name="status_bar_phone_evdo_signal">phone_evdo_signal</string> <string translatable="false" name="status_bar_phone_signal">phone_signal</string> <string translatable="false" name="status_bar_battery">battery</string> <string translatable="false" name="status_bar_alarm_clock">alarm_clock</string> <string translatable="false" name="status_bar_secure">secure</string> <string translatable="false" name="status_bar_clock">clock</string> <string translatable="false" name="status_bar_mobile">mobile</string> <string translatable="false" name="status_bar_vpn">vpn</string> <string translatable="false" name="status_bar_ethernet">ethernet</string> <string translatable="false" name="status_bar_airplane">airplane</string>
好了,到这里我们的第一部分初始化流程就讲完了
四、状态显示流程
由上面的初始化流程我们可以知道,每个icon都对应了slot,slot数量比较多,我们就挑一个常见的Headset讲下,其他的流程都是大致一样的。
1、PhoneStatusBarPolicy
初始化注册了大量的监听
①init
// 初始化headset的slot mSlotHeadset = resources.getString(com.android.internal.R.string.status_bar_headset); /** Initialize the object after construction. */ public void init() { // listen for broadcasts IntentFilter filter = new IntentFilter(); // 注册headset状态变化的action filter.addAction(AudioManager.ACTION_HEADSET_PLUG); filter.addAction(Intent.ACTION_SIM_STATE_CHANGED); filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED); filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE); filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler); Observer<Integer> observer = ringer -> mHandler.post(this::updateVolumeZen); mRingerModeTracker.getRingerMode().observeForever(observer); mRingerModeTracker.getRingerModeInternal().observeForever(observer); .... }
②BroadcastReceiver
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); switch (action) { case Intent.ACTION_SIM_STATE_CHANGED: // Avoid rebroadcast because SysUI is direct boot aware. if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) { break; } break; case TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED: updateTTY(intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE, TelecomManager.TTY_MODE_OFF)); break; case Intent.ACTION_MANAGED_PROFILE_AVAILABLE: case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE: case Intent.ACTION_MANAGED_PROFILE_REMOVED: updateManagedProfile(); break; //监听ACTION_HEADSET_PLUG case AudioManager.ACTION_HEADSET_PLUG: updateHeadsetPlug(context, intent); break; } } };
③updateHeadsetPlug
完成icon添加和状态监听,然后当收到对应的action变化的时候,更新headset icon
private void updateHeadsetPlug(Context context, Intent intent) { boolean connected = intent.getIntExtra("state", 0) != 0; boolean hasMic = intent.getIntExtra("microphone", 0) != 0; if (connected) { String contentDescription = mResources.getString(hasMic ? R.string.accessibility_status_bar_headset : R.string.accessibility_status_bar_headphones); //setIcon负责设置icon mIconController.setIcon(mSlotHeadset, hasMic ? R.drawable.stat_sys_headset_mic : R.drawable.stat_sys_headset, contentDescription); //setIconVisibility则根据connected状态设置icon的可见性 mIconController.setIconVisibility(mSlotHeadset, true); } else { /*UNISOC: Add for bug 1130932 {@ */ AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); if (!audioManager.isWiredHeadsetOn()) { //setIconVisibility则根据connected状态设置icon的可见性 mIconController.setIconVisibility(mSlotHeadset, false); } /* @} */ } }
2、StatusBarIconControllerImpl
①setIcon
@Override public void setIcon(String slot, int resourceId, CharSequence contentDescription) { //根据slot找到对应的index int index = getSlotIndex(slot); //用index取得对应的icon StatusBarIconHolder holder = getIcon(index, 0); if (holder == null) { //第一次的时候,icon == null,所以通过new StatusBarIcon创建一个 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); } } @Override 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); //onIconAdded-》初始化流程里面StatusBarIconController中添加icon的入口 //到这里setIcon就添加完毕了 mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, holder)); }
②setIconVisibility
public void setIconVisibility(String slot, boolean visibility) { int index = getSlotIndex(slot); StatusBarIconHolder holder = getIcon(index, 0); if (holder == null || holder.isVisible() == visibility) { return; } holder.setVisible(visibility); //icon已经创建成功了,icon非空,并且第一次是icon.visible != visibility的 //就会顺利的走到handleSet(index, icon) handleSet(index, holder); } private void handleSet(int index, StatusBarIconHolder holder) { int viewIndex = getViewIndex(index, holder.getTag()); //初始化流程里面StatusBarIconController中setIcon的地方 //到这里setIconVisibility也设置完毕了 mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder)); }
五、创建状态栏的窗口
1、StatusBarWindowController
mStatusBarWindowController.attach()
/** * Adds the status bar view to the window manager. */ public void attach() { // 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, mBarHeight, WindowManager.LayoutParams.TYPE_STATUS_BAR, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, PixelFormat.TRANSLUCENT); mLp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC; mLp.token = new Binder(); mLp.gravity = Gravity.TOP; mLp.setFitInsetsTypes(0 /* types */); mLp.setTitle("StatusBar"); mLp.packageName = mContext.getPackageName(); mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; //WindowManager中添加view //mStatusBarView = mSuperStatusBarViewFactory.getStatusBarWindowView(); //private final SuperStatusBarViewFactory mSuperStatusBarViewFactory; mWindowManager.addView(mStatusBarView, mLp); mLpChanged.copyFrom(mLp); }
2、SuperStatusBarViewFactory
/** * Gets the inflated {@link StatusBarWindowView} from {@link R.layout#super_status_bar}. * Returns a cached instance, if it has already been inflated. */ public StatusBarWindowView getStatusBarWindowView() { if (mStatusBarWindowView != null) { return mStatusBarWindowView; } //由其可知加载的布局来自于super_status_bar mStatusBarWindowView = (StatusBarWindowView) mInjectionInflationController.injectable( LayoutInflater.from(mContext)).inflate(R.layout.super_status_bar, /* root= */ null); if (mStatusBarWindowView == null) { throw new IllegalStateException( "R.layout.super_status_bar could not be properly inflated"); } return mStatusBarWindowView; }
3、super_status_bar.xml
从前面可知这里会用CollapsedStatusBarFragment 替换 status_bar_container(状态栏通知显示区域),完成图标的显示
<!-- This is the status bar window. --> <com.android.systemui.statusbar.phone.StatusBarWindowView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:sysui="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <FrameLayout android:id="@+id/status_bar_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="gone" /> <FrameLayout android:id="@+id/car_top_navigation_bar_container" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout> </com.android.systemui.statusbar.phone.StatusBarWindowView>