首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android 8.0 SystemUI(四):二说顶部 StatusBar

Android 8.0 SystemUI(四):二说顶部 StatusBar

作者头像
菜天哥哥
发布2018-09-29 15:38:48
4.4K3
发布2018-09-29 15:38:48
举报
文章被收录于专栏:猿湿Xoong猿湿Xoong猿湿Xoong

点击蓝字关注“猿湿Xoong”

一个爱折腾爱分享的技术公众号

大家好,我是ptt,本篇是 SystemUI 的第四篇,也是 StatusBar 的第二说。

接着上一说的 StatusBar 之 StatusIcon,这篇说一说 StatusBar 的 Signal Cluster - 状态栏上显示wifi、手机等信号状态的地方。

即上图中「箭头3」指向的地方。

目录

老规矩,先上目录。

大体框架

SignalCluster 包含的图标有:

V**提示、Ethernet图标、Wifi图标、Airplane提示、NoSim提示,移动网络图标等共6类图标。

在代码中,关于信号图标的核心类是 SignalClusterView.java,这个类和布局文件 res/layout/signal_cluster_view.xml 高度相关。Signal相关的6类图标均在其中。对应布局加载路径如下。

而 status_bar.xml 被 CollapsedStatusBarFragment 加载。在它的onViewCreated 中,通过 id - signal_cluster 找到布局并inflate。

private SignalClusterView mSignalClusterView;

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    ...
    // 此处加载SignalClusterView
    mSignalClusterView = mStatusBar.findViewById(R.id.signal_cluster);
    Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mSignalClusterView);
    ...
}

既然Signal相关的核心类是SignalClusterView,T哥画了一个大概的类图。

根据上图,从SignalClusterView开始辐射研究。

SignalClusterView继承LinearLayout,并实现了接口NetworkController的内部接口SignalCallback。此接口的方法,主要是用来更新对应View。

public interface SignalCallback {
    default void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
            boolean activityIn, boolean activityOut, String description, boolean isTransient) {}
    default void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
            int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
            String description, boolean isWide, int subId, boolean roaming) {}
    default void setSubs(List<SubscriptionInfo> subs) {}
    default void setNoSims(boolean show, boolean simDetected) {}
    default void setEthernetIndicators(IconState icon) {}
    default void setIsAirplaneMode(IconState icon) {}
    default void setMobileDataEnabled(boolean enabled) {}
}

在SignalClusterView的构造方法中,获得了NetworkControllerImpl对象。

...
private final NetworkController mNetworkController;
...
public SignalClusterView(Context context, AttributeSet attrs, int defStyle) {
    ...
    mNetworkController = Dependency.get(NetworkController.class);
    ...
}

NetworkControllerImpl,顾名思义,是接口NetworkController的实现类,聚合了各种信号controller(sub controller),并通过广播监听各类信号变化。当有变化时,调用对应 sub controller 的对应方法更新State。

各类sub controller,根据不同 state 提供对应 Icon。通过注册进来的SignalCallback,不同controller调用不同回调方法。在SignalClusterView中的回调实现中,将Icon图标设置入ImageView中。

sub controller 大体介绍如下。

// 用于wifi状态更新,提供对应图标
WifiSignalController
// 用于移动信号状态更新,提供对应图标
MobileSignalController
// 用于有线网络(网线)状态更新,并提供对应图标
EthernetSignalController

而 SecurityControllerImpl 负责 V** 状态的管理和获取。

初始化

Signal相关的图标是如何被初始化的?

在SignalClusterView.java中,当布局从xml中加载完成时, find view by id 初始化各个Signal对应的View。

@Override
protected void onFinishInflate() {
    super.onFinishInflate();

    mV**            = findViewById(R.id.V**);
    mEthernetGroup  = findViewById(R.id.ethernet_combo);
    mEthernet       = findViewById(R.id.ethernet);
    mEthernetDark   = findViewById(R.id.ethernet_dark);
    mWifiGroup      = findViewById(R.id.wifi_combo);
    mWifi           = findViewById(R.id.wifi_signal);
    mWifiDark       = findViewById(R.id.wifi_signal_dark);
    mWifiActivityIn = findViewById(R.id.wifi_in);
    mWifiActivityOut= findViewById(R.id.wifi_out);
    mAirplane       = findViewById(R.id.airplane);
    mNoSims         = findViewById(R.id.no_sims);
    mNoSimsDark     = findViewById(R.id.no_sims_dark);
    mNoSimsCombo    =             findViewById(R.id.no_sims_combo);
    mWifiAirplaneSpacer =         findViewById(R.id.wifi_airplane_spacer);
    mWifiSignalSpacer =           findViewById(R.id.wifi_signal_spacer);
    mMobileSignalGroup =          findViewById(R.id.mobile_signal_group);

    maybeScaleV**AndNoSimsIcons();
}

在onAttachedToWindow方法中,进行所有 signal icon 的初始化。但是V** 和 其他 Signal 有不同。

@Override
protected void onAttachedToWindow() {
    ...
    // 初始化代表V**的变量
    mV**Visible = mSecurityController.isV**Enabled();
    ...

    // apply - 根据状态,进行一遍所有view的更新。
    apply();
    ...
    // 添加回调
    mNetworkController.addCallback(this);
    mSecurityController.addCallback(this);
}

V** 的状态可以直接通过SecurityController获取到。所以此处直接初始化,并调用apply刷新。

而其他所有 Signal 的初始化,则在NetworkControllerImpl 的 addCallback方法中,每一行都是在初始化。并通过cb回调到SignalClusterView的相关实现。最后一个保存当前回调,待有状态变化时更新。

public void addCallback(SignalCallback cb) {
    cb.setSubs(mCurrentSubscriptions);
    cb.setIsAirplaneMode(new IconState(mAirplaneMode,
            TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext));
    cb.setNoSims(mHasNoSubs, mSimDetected);
    mWifiSignalController.notifyListeners(cb);
    mEthernetSignalController.notifyListeners(cb);
    for (int i = 0; i < mMobileSignalControllers.size(); i++) {
        MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
        mobileSignalController.notifyListeners(cb);
    }
    mCallbackHandler.setListening(cb, true);
}

状态更新

对于非V**的Signal更新,是在NetworkControllerImpl中,通过注册广播的方式进行监听 - 除了手机网络是通过PhoneStateListener。收到广播后,调用回调更新,方式比较简单,这里不再累赘。

private void registerListeners() {
    for (int i = 0; i < mMobileSignalControllers.size(); i++) {
        MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
        mobileSignalController.registerListener();
    }
    ...
    // broadcasts
    IntentFilter filter = new IntentFilter();
    filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
    filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
    filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
    filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
    filter.addAction(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
    filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
    filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
    filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
    filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
    filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
    filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
    mContext.registerReceiver(this, filter, null, mReceiverHandler);
    ...
}

而对于V**的监听,则是在SecurityControllerImpl中,通过ConnectivityManager对特定网络注册回调,实现监听。

···
private static final NetworkRequest REQUEST = new NetworkRequest.Builder()
        .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_V**)
        .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
        .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
        .build();

···
private final NetworkCallback mNetworkCallback = new NetworkCallback() {
    @Override
    public void onAvailable(Network network) {
        if (DEBUG) Log.d(TAG, "onAvailable " + network.netId);
        updateState();
        fireCallbacks();
    };

    // TODO Find another way to receive V** lost.  This may be delayed depending on
    // how long the V** connection is held on to.
    @Override
    public void onLost(Network network) {
        if (DEBUG) Log.d(TAG, "onLost " + network.netId);
        updateState();
        fireCallbacks();
    };
};

...
public SecurityControllerImpl(Context context, SecurityControllerCallback callback) {
    ...
    // TODO: re-register network callback on user change.
    mConnectivityManager.registerNetworkCallback(REQUEST, mNetworkCallback);
   ...
}

其中REQUEST代表着第三方App的V**网络类型。

当有满足REQUEST类型的网络时,mNetworkCallback的onAvaiable就会被调用。此时通过注册进入的SecurityControllerCallback,用以更新V**。

或者,当满足REQUEST类型的网络丢失时,mNetworkCallback的onLost会被调用, 从而更新状态栏,去除V**图标。

T哥还知道,平日里,此类方法的另一个常用场景是:当应用仅想通过wifi或仅仅想通过移动数据进行网络请求时,则构造一个对应网络类型的request,一个callback,并通过CM的registerNetworkCallback进行注册。待callback的onAvaiable被回调,通过cm.bindProcessToNetwork申请你想要的网络。

最后

这是SystemUI系列的第四篇。觉得T哥写的东西对你有价值,欢迎关注。

推荐阅读

Android 8.0 SystemUI(三):一说顶部 StatusBar

Android SystemUI(二):启动流程和初始化

Android SystemUI(一):图文并茂的介绍 :D

1024G免费IT资源共享!Android、Java、C、C++、Linux、数据库、人工智能等等领域基础及进阶学习资料,后台回复「1024」就能免费获取!

--- End ---

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-08-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 菜天 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档