Android14 StatusBar顶部图标加载流程

前言

说到 StatusBar 的顶部图标,就不得不提起以下两个类:

  • PhoneStatusBarPolicy:定义了系统通知图标的设置策略;
  • StatusBarSignalPolicy:定义了状态栏网络信号策略;

StatusBarSignalPolicy 前面有过分析,这次以 PhoneStatusBarPolicy 为例进行分析;
PhoneStatusBarPolicy 在 CentralSurfacesImpl 的 start() 方法里初始化:
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java

@Override
public void start() {
    // 省略部分代码......
    mStatusBarSignalPolicy.init();  // 这里Android 14 与前面 Android 11 的初始化方法有点不同。
    // 创建整个SystemUI视图并添加到WindowManager中
    createAndAddWindows(result);//这个重点方法,创建相关的视图
    // 省略部分代码......
    // 调用图标策略来显示更新所有图标。
    mIconPolicy.init();
    // 省略部分代码......
}

这里直接看PhoneStatusBarPolicy#init() 这个方法很关键
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/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()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/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 里面的控件。
frameworks/base/packages/SystemUI/res/layout/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
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/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 中的顺序。
frameworks/base/core/res/res/values/config.xml

<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 的加载显示和移除。

相关推荐

  1. Android14 StatusBar顶部图标流程

    2024-04-23 22:24:01       12 阅读
  2. Android批量图片OOM问题

    2024-04-23 22:24:01       31 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-23 22:24:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-23 22:24:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-23 22:24:01       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-23 22:24:01       20 阅读

热门阅读

  1. Spring Cloud Eureka面试题

    2024-04-23 22:24:01       13 阅读
  2. 【设计模式】观察者模式

    2024-04-23 22:24:01       13 阅读
  3. ecs150 project2 Unix Shell

    2024-04-23 22:24:01       13 阅读
  4. Vue3+Ant Design 父组件调用子组件方法

    2024-04-23 22:24:01       14 阅读
  5. 基于深度神经网络的图像识别技术研究

    2024-04-23 22:24:01       13 阅读
  6. vue-router学习4:嵌套路由

    2024-04-23 22:24:01       12 阅读
  7. nginx配置不同设备访问不同地址

    2024-04-23 22:24:01       16 阅读