学习笔记:
说到 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 的加载显示和移除。