学习笔记:
说到 StatusBar 的顶部图标,就不得不提起以下两个类:
- PhoneStatusBarPolicy:定义了系统通知图标的设置策略;
- StatusBarSignalPolicy:定义了状态栏网络信号策略;
StatusBarSignalPolicy 前面有过分析,这次以 PhoneStatusBarPolicy 为例进行分析;
PhoneStatusBarPolicy 在 StatusBar 的 start() 方法里初始化:
// StatusBar.java @Override public void start() { // 省略部分代码...... mStatusBarSignalPolicy.init(); // 这里Android 13 与前面 Android 11 的初始化方法有点不同。 // 创建整个SystemUI视图并添加到WindowManager中 createAndAddWindows();//这个重点方法,创建相关的视图 // 省略部分代码...... // 调用图标策略来显示更新所有图标。 mIconPolicy.init(); // 省略部分代码...... }
这里直接看PhoneStatusBarPolicy#init() 这·个·方法很关键:
// PhoneStatusBarPolicy.java public void init() { // 注册广播监听。 IntentFilter filter = new IntentFilter(); // 耳机是否插入 filter.addAction(AudioManager.ACTION_HEADSET_PLUG); // SIM卡广播 filter.addAction(Intent.ACTION_SIM_STATE_CHANGED); // TTY 模式已更改,(文字电话) 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); /// 添加用户切换操作以更新可能的警报图标 filter.addAction(Intent.ACTION_USER_SWITCHED); // 向处理程序注册接收器 mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler,UserHandle.ALL); // 注册闹钟已更改 registerAlarmClockChanged(UserHandle.USER_OWNER, false); Observer<Integer> observer = ringer -> mHandler.post(this::updateVolumeZen); mRingerModeTracker.getRingerMode().observeForever(observer); mRingerModeTracker.getRingerModeInternal().observeForever(observer); // 监听用户配置文件更改. try { mIActivityManager.registerUserSwitchObserver(mUserSwitchListener, TAG); } catch (RemoteException e) { // Ignore } // TTY状态 updateTTY(); // 蓝牙状态 updateBluetooth(); //eMBMS状态 mIconController.setIcon(mSlotEmbms, R.drawable.stat_sys_embms, null); mIconController.setIconVisibility(mSlotEmbms, false); // 闹钟 mIconController.setIcon(mSlotAlarmClock, R.drawable.stat_sys_alarm, null); mIconController.setIconVisibility(mSlotAlarmClock, false); // zen mIconController.setIcon(mSlotZen, R.drawable.stat_sys_dnd, null); mIconController.setIconVisibility(mSlotZen, false); // 振动 mIconController.setIcon(mSlotVibrate, R.drawable.stat_sys_ringer_vibrate, mResources.getString(R.string.accessibility_ringer_vibrate)); mIconController.setIconVisibility(mSlotVibrate, false); // mute mIconController.setIcon(mSlotMute, R.drawable.stat_sys_ringer_silent, mResources.getString(R.string.accessibility_ringer_silent)); mIconController.setIconVisibility(mSlotMute, false); updateVolumeZen(); // cast mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast, null); mIconController.setIconVisibility(mSlotCast, false); // 热点 mIconController.setIcon(mSlotHotspot, R.drawable.stat_sys_hotspot, mResources.getString(R.string.accessibility_status_bar_hotspot)); mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled()); // managed profile updateManagedProfile(); // 数据保护程序 mIconController.setIcon(mSlotDataSaver, R.drawable.stat_sys_data_saver, mResources.getString(R.string.accessibility_data_saver_on)); mIconController.setIconVisibility(mSlotDataSaver, false); // 隐私 items String microphoneString = mResources.getString(PrivacyType.TYPE_MICROPHONE.getNameId()); String microphoneDesc = mResources.getString( R.string.ongoing_privacy_chip_content_multiple_apps, microphoneString); mIconController.setIcon(mSlotMicrophone, PrivacyType.TYPE_MICROPHONE.getIconId(), microphoneDesc); mIconController.setIconVisibility(mSlotMicrophone, false); String cameraString = mResources.getString(PrivacyType.TYPE_CAMERA.getNameId()); String cameraDesc = mResources.getString( R.string.ongoing_privacy_chip_content_multiple_apps, cameraString); mIconController.setIcon(mSlotCamera, PrivacyType.TYPE_CAMERA.getIconId(), cameraDesc); mIconController.setIconVisibility(mSlotCamera, false); mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID, mResources.getString(R.string.accessibility_location_active)); mIconController.setIconVisibility(mSlotLocation, false); // 传感器关闭 mIconController.setIcon(mSlotSensorsOff, R.drawable.stat_sys_sensors_off, mResources.getString(R.string.accessibility_sensors_off_active)); mIconController.setIconVisibility(mSlotSensorsOff, mSensorPrivacyController.isSensorPrivacyEnabled()); // 录屏 mIconController.setIcon(mSlotScreenRecord, R.drawable.stat_sys_screen_record, null); mIconController.setIconVisibility(mSlotScreenRecord, false); mRotationLockController.addCallback(this); mBluetooth.addCallback(this); mProvisionedController.addCallback(this); mZenController.addCallback(this); mCast.addCallback(mCastCallback); mHotspot.addCallback(mHotspotCallback); mNextAlarmController.addCallback(mNextAlarmCallback); mDataSaver.addCallback(this); mKeyguardStateController.addCallback(this); mPrivacyItemController.addCallback(this); mSensorPrivacyController.addCallback(mSensorPrivacyListener); mLocationController.addCallback(this); mRecordingController.addCallback(this); mCommandQueue.addCallback(this); }
每个icon对应一个updatexxx(),依靠监听和回调机制,可以实现控制状态栏icon图标的显示、隐藏。
上述就是图标的加载显示和移除了,那么布局又是在哪添加的呢??
下面接着看 CollapsedStatusBarFragment ,启动方式前面讲过,这里不在重复;看其的 onViewCreated():
CollapsedStatusBarFragment #onViewCreated()
// CollapsedStatusBarFragment.java @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create(this); mStatusBarFragmentComponent.init(); mStatusBar = (PhoneStatusBarView) view; View contents = mStatusBar.findViewById(R.id.status_bar_contents); contents.addOnLayoutChangeListener(mStatusBarLayoutListener); updateStatusBarLocation(contents.getLeft(), contents.getRight()); if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) { mStatusBar.restoreHierarchyState( savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE)); } mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons), mFeatureFlags); mDarkIconManager.setShouldLog(true); updateBlockedIcons(); mStatusBarIconController.addIconGroup(mDarkIconManager); mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area); mClockView = mStatusBar.findViewById(R.id.clock); mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip); showSystemIconArea(false); showClock(false); initEmergencyCryptkeeperText(); initOperatorName(); initNotificationIconArea(); mSystemEventAnimator = new StatusBarSystemEventAnimator(mSystemIconArea, getResources()); mCarrierConfigTracker.addCallback(mCarrierConfigCallback); mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener); }
补充:在 status_bar.xml 中
- system_icon_area:就是显示蓝牙、wifi、VPN、网卡icon那块区域。
接着我看可以注意到这么几句代码:
mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons), mFeatureFlags); mDarkIconManager.setShouldLog(true); updateBlockedIcons(); // 添加图标组 mStatusBarIconController.addIconGroup(mDarkIconManager);
上述 R.id.statusIcons 即是 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="wrap_content" android:layout_height="match_parent" android:layout_gravity="center_vertical|end" android:gravity="center_vertical"> <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.battery.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>
我们接着看 StatusBarIconControllerImpl 的构造函数,因为 mStatusBarIconController 是一个接口,而其的实现类是 StatusBarIconControllerImpl。
// StatusBarIconControllerImpl.java @Inject public StatusBarIconControllerImpl( Context context, CommandQueue commandQueue, DemoModeController demoModeController, ConfigurationController configurationController, TunerService tunerService, DumpManager dumpManager) { super(context.getResources().getStringArray( com.android.internal.R.array.config_statusBarIcons),context); // 省略部分代码...... }
我们可以发现状态栏 icon 加载的图标来源于framework/base/core/res/res/values/config.xml 文件在这里我们就找到了 index 和 slot 的出处,原来在初始化的时候就已经定义好了所有的 slots,然后从 framework 中加载出来,index 就是 string-array 中的顺序。
<string-array name="config_statusBarIcons"> <item><xliff:g id="id">@string/status_bar_alarm_clock</xliff:g></item> <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_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> ................ </string-array>
然后是 StatusBarIconControllerImpl.java 这个控制器来控制 icon 的加载显示和移除。