Android 13.0 StatusBar顶部图标加载流程

学习笔记:

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,193评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,306评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,130评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,110评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,118评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,085评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,007评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,844评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,283评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,508评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,395评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,985评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,630评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,797评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,653评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,553评论 2 352

推荐阅读更多精彩内容