当前位置: 移动技术网 > IT编程>移动开发>Android > Android8.1 MTK平台 SystemUI源码分析之 网络信号栏显示刷新

Android8.1 MTK平台 SystemUI源码分析之 网络信号栏显示刷新

2019年06月05日  | 移动技术网IT编程  | 我要评论

中国美术学院天空论坛,吉泽明步 超m自缚痴女,激清戏

systemui系列文章

android8.1 mtk平台 systemui源码分析之 notification流程

android8.1 mtk平台 systemui源码分析之 电池时钟刷新

android 8.1平台systemui 导航栏加载流程解析

一、从布局说起

前面的文章分析过,网络信号栏这块属于 system_icon_area,里面包含蓝牙、wifi、vpn、网卡、sim卡网络类型、

数据流量符号、sim卡信号格、电池、时钟。

先来看下 system_icon_area 对应的布局文件 system_icons.xml

<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/system_icons"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:gravity="center_vertical">

    <com.android.keyguard.alphaoptimizedlinearlayout 
        android:id="@+id/statusicons"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:gravity="center_vertical"
        android:orientation="horizontal"/>

    <include layout="@layout/signal_cluster_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginstart="@dimen/signal_cluster_margin_start"/>

    <com.android.systemui.batterymeterview 
        android:id="@+id/battery"
        android:layout_height="match_parent"
        android:layout_width="0dp"
        />
</linearlayout>

看到里面的 signal_cluster_view.xml 正是我们要找的信号栏布局文件,内容有点多,下面只截取我们关心的

vendor\mediatek\proprietary\packages\apps\systemui\res\layout\signal_cluster_view.xml

<com.android.systemui.statusbar.signalclusterview
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/signal_cluster"
    android:layout_height="match_parent"
    android:layout_width="wrap_content"
    android:gravity="center_vertical"
    android:orientation="horizontal"
    android:paddingend="@dimen/signal_cluster_battery_padding"
    >
   ... vpn 

   ... 网卡

   ... wifi

    手机信号栏
    <linearlayout
        android:id="@+id/mobile_signal_group"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        >
    </linearlayout>

    未插入sim卡
    <framelayout
        android:id="@+id/no_sims_combo"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:contentdescription="@string/accessibility_no_sims">
        <com.android.systemui.statusbar.alphaoptimizedimageview
            android:theme="?attr/lighticontheme"
            android:id="@+id/no_sims"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:src="@drawable/stat_sys_no_sims"
            />
        <com.android.systemui.statusbar.alphaoptimizedimageview
            android:theme="?attr/darkicontheme"
            android:id="@+id/no_sims_dark"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:src="@drawable/stat_sys_no_sims"
            android:alpha="0.0"
            />
    </framelayout>
    <view
        android:id="@+id/wifi_airplane_spacer"
        android:layout_width="@dimen/status_bar_airplane_spacer_width"
        android:layout_height="4dp"
        android:visibility="gone"
        />

    飞行模式
    <imageview
        android:id="@+id/airplane"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        />
</com.android.systemui.statusbar.signalclusterview>

可以看到最外层是自定义 signalclusterview,xml里包含了 vpn、网卡、wifi、手机信号栏、未插入sim卡、飞行模式对应的 view,那么接下来看下 signalclusterview 代码

vendor\mediatek\proprietary\packages\apps\systemui\src\com\android\systemui\statusbar\signalclusterview.java

public class signalclusterview extends linearlayout implements networkcontrollerimpl.signalcallback, securitycontroller.securitycontrollercallback, tunable,darkreceiver 

public signalclusterview(context context, attributeset attrs, int defstyle) {
    super(context, attrs, defstyle);

    resources res = getresources();
    mmobilesignalgroupendpadding =
            res.getdimensionpixelsize(r.dimen.mobile_signal_group_end_padding);
    mmobiledataiconstartpadding =
            res.getdimensionpixelsize(r.dimen.mobile_data_icon_start_padding);
    mwidetypeiconstartpadding = res.getdimensionpixelsize(r.dimen.wide_type_icon_start_padding);
    msecondarytelephonypadding = res.getdimensionpixelsize(r.dimen.secondary_telephony_padding);
    mendpadding = res.getdimensionpixelsize(r.dimen.signal_cluster_battery_padding);
    mendpaddingnothingvisible = res.getdimensionpixelsize(
            r.dimen.no_signal_cluster_battery_padding);

    typedvalue typedvalue = new typedvalue();
    res.getvalue(r.dimen.status_bar_icon_scale_factor, typedvalue, true);
    miconscalefactor = typedvalue.getfloat();
    //网络相关控制器
    mnetworkcontroller = dependency.get(networkcontroller.class);
    //安全相关控制器
    msecuritycontroller = dependency.get(securitycontroller.class);
    updateactivityenabled();

    /// m: add for plugin feature @ {
    mstatusbarext = opsystemuicustomizationfactorybase.getopfactory(context)
                                 .makesystemuistatusbar(context);
    /// @ }
    miswfcenable = systemproperties.get("persist.mtk_wfc_support").equals("1");
}

看到 signalclusterview 继承自 linearlayout,实现了 networkcontroller、securitycontroller(这俩类控制图标的刷新逻辑)

构造方法中通过 dependency.get() 实例化 networkcontroller、securitycontroller这俩核心

类,跟进 dependent 类中大概看了下 get()方法,其实就是通过单例模式来进行管理,里面维护了一

个数据类型为 arraymap 的 dependencyprovider 集合对象,通过 put和 get 来存取。

接着回到 signalclusterview 中看下控件都是怎么初始化的?

 @override
protected void onfinishinflate() {
    super.onfinishinflate();

    mvpn            = findviewbyid(r.id.vpn);
    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);

    maybescalevpnandnosimsicons();
}

这里初始化了一堆刚刚布局文件里的控件,onfinishinflate() 在 xml 布局文件被加载完成后就会调

用,我们看到布局文件中都没有给控件设置对应的 background icon,而且有的 visibility 为

gone,那么信号栏图标是如何设置对应的icon和显示的呢?

二、signalcluterview 详解

在这里插入图片描述

signalcluterview 中调用频率很高的方法 apply() 就是幕后黑手,通过该方法控制一系列图标的更

新, 然而 signalcallback 的如下每个回调最终都调用 apply()

1、setwifiindicators() wifi开关状态、流量上下行

2、setmobiledataindicators() 手机网络类型、信号强度、流量上下行、volte图标

3、setsubs() sim卡识别结束

4、setnosims() 未插入sim卡状态

5、setethernetindicators() 网卡状态

6、setisairplanemode() 飞行模式是否打开

7、setmobiledataenabled() sim卡数据流量是否开启

1、apply()

private void apply() {
    if (mwifigroup == null) return;

    //vpn图标
    if (mvpnvisible) {
        if (mlastvpniconid != mvpniconid) {
            seticonforview(mvpn, mvpniconid);
            mlastvpniconid = mvpniconid;
        }
        miconlogger.oniconshown(slot_vpn);
        mvpn.setvisibility(view.visible);
    } else {
        miconlogger.oniconhidden(slot_vpn);
        mvpn.setvisibility(view.gone);
    }
    if (debug) log.d(tag, string.format("vpn: %s", mvpnvisible ? "visible" : "gone"));

    //网卡图标
    if (methernetvisible) {
        if (mlastetherneticonid != metherneticonid) {
            seticonforview(methernet, metherneticonid);
            seticonforview(methernetdark, metherneticonid);
            mlastetherneticonid = metherneticonid;
        }
        methernetgroup.setcontentdescription(methernetdescription);
        miconlogger.oniconshown(slot_ethernet);
        methernetgroup.setvisibility(view.visible);
    } else {
        miconlogger.oniconhidden(slot_ethernet);
        methernetgroup.setvisibility(view.gone);
    }

    if (debug) log.d(tag,
            string.format("ethernet: %s",
                (methernetvisible ? "visible" : "gone")));
    //wifi图标
    if (mwifivisible) {
        if (mwifistrengthid != mlastwifistrengthid) {
            seticonforview(mwifi, mwifistrengthid);
            seticonforview(mwifidark, mwifistrengthid);
            mlastwifistrengthid = mwifistrengthid;
        }
        miconlogger.oniconshown(slot_wifi);
        mwifigroup.setcontentdescription(mwifidescription);
        mwifigroup.setvisibility(view.visible);
    } else {
        miconlogger.oniconhidden(slot_wifi);
        mwifigroup.setvisibility(view.gone);
    }

    if (debug) log.d(tag,
            string.format("wifi: %s sig=%d",
                (mwifivisible ? "visible" : "gone"),
                mwifistrengthid));
    //wifi数据上下行图标
    mwifiactivityin.setvisibility(mwifiin ? view.visible : view.gone);
    mwifiactivityout.setvisibility(mwifiout ? view.visible : view.gone);

    boolean anymobilevisible = false;
    /// m: support for [network type on statusbar]
    /// a spacer is set between networktype and wifi icon @ {
    if (featureoptions.mtk_cta_set) {
        anymobilevisible = true;
    }
    /// @ }
    //sim 卡组图标
    int firstmobiletypeid = 0;
    for (phonestate state : mphonestates) {
        //phonestate中的另一个apply()方法,对应网络类型、信号格等
        if (state.apply(anymobilevisible)) {
            if (!anymobilevisible) {
                firstmobiletypeid = state.mmobiletypeid;
                anymobilevisible = true;
            }
        }
    }
    if (anymobilevisible) {
        miconlogger.oniconshown(slot_mobile);
    } else {
        miconlogger.oniconhidden(slot_mobile);
    }
    //飞行模式图标
    if (misairplanemode) {
        if (mlastairplaneiconid != mairplaneiconid) {
            seticonforview(mairplane, mairplaneiconid);
            mlastairplaneiconid = mairplaneiconid;
        }
        mairplane.setcontentdescription(mairplanecontentdescription);
        miconlogger.oniconshown(slot_airplane);
        mairplane.setvisibility(visible);
    } else {
        miconlogger.oniconhidden(slot_airplane);
        mairplane.setvisibility(view.gone);
    }
    //wifi和飞行模式间隔
    if (misairplanemode && mwifivisible) {
        mwifiairplanespacer.setvisibility(view.visible);
    } else {
        mwifiairplanespacer.setvisibility(view.gone);
    }

    if (((anymobilevisible && firstmobiletypeid != 0) || mnosimsvisible) && mwifivisible) {
        mwifisignalspacer.setvisibility(view.visible);
    } else {
        mwifisignalspacer.setvisibility(view.gone);
    }
    //未插入sim卡图标组
    if (mnosimsvisible) {
        miconlogger.oniconshown(slot_mobile);
        mnosimscombo.setvisibility(view.visible);
        if (!objects.equals(msimdetected, mnosimscombo.gettag())) {
            mnosimscombo.settag(msimdetected);
            /// m:alps03596830 don't show lack of signal when airplane mode is on.
            if (msimdetected && !misairplanemode) {
                signaldrawable d = new signaldrawable(mnosims.getcontext());
                d.setdarkintensity(0);
                mnosims.setimagedrawable(d);
                mnosims.setimagelevel(signaldrawable.getemptystate(4));

                signaldrawable dark = new signaldrawable(mnosims.getcontext());
                dark.setdarkintensity(1);
                mnosimsdark.setimagedrawable(dark);
                mnosimsdark.setimagelevel(signaldrawable.getemptystate(4));
            } else {
                mnosims.setimageresource(r.drawable.stat_sys_no_sims);
                mnosimsdark.setimageresource(r.drawable.stat_sys_no_sims);
            }
        }
    } else {
        miconlogger.oniconhidden(slot_mobile);
        mnosimscombo.setvisibility(view.gone);
    }

    /// m: add for plugin feature @ {
    mstatusbarext.setcustomizednosimsvisible(mnosimsvisible);
    mstatusbarext.setcustomizedairplaneview(mnosimscombo, misairplanemode);
    /// @ }

    boolean anythingvisible = mnosimsvisible || mwifivisible || misairplanemode
            || anymobilevisible || mvpnvisible || methernetvisible;
    setpaddingrelative(0, 0, anythingvisible ? mendpadding : mendpaddingnothingvisible, 0);
}

通过上面的代码发现 apply 控制了 vpn、网卡、wifi、飞行模式、未插入sim 这几种图标的显示和隐

藏,我们看到里面有另外一个 state.apply(anymobilevisible) 用来控制 sim卡相关的图标,接下

来看下都有哪些图标呢?

2、内部类 phonestate

phonestate 是 signalclusterview 中一个内部类,控制volte、网络类型、数据是否打开、信号格

数、漫游等图标

private class phonestate {
    //sim卡id
    private final int msubid;
    //sim卡组是否可见
    private boolean mmobilevisible = false;
    //信号格数图标、数据流量是否打开图标(关闭是x,打开是网络类型小图标4g/3g)
    private int mmobilestrengthid = 0, mmobiletypeid = 0;
    ///m: add for [network type and volte on statusbar]
    //网络类型图标
    private int mnetworkicon = 0;
    //volte图标
    private int mvolteicon = 0;
    
    private int mlastmobilestrengthid = -1;
    private int mlastmobiletypeid = -1;
    private boolean mismobiletypeiconwide;
    //网络类型描述,运营商类型,或只能拨打紧急号码
    private string mmobiledescription, mmobiletypedescription;

    //整个phonestate根本局,sim信号栏组
    private viewgroup mmobilegroup;
    //信号格控件、流量图标控件、是否漫游控件
    private imageview mmobile, mmobiledark, mmobiletype, mmobileroaming;
    public boolean mroaming;
    //手机流量上下行控件
    private imageview mmobileactivityin;
    private imageview mmobileactivityout;

    public boolean mactivityin;
    public boolean mactivityout;
    /// m: add for new features @ {
    // add for [network type and volte on statusbar]
    //网络类型控件
    private imageview mnetworktype;
    //volte控件
    private imageview mvoltetype;
    private boolean miswfccase;
    /// @ }

    /// m: add for plugin features. @ {
    private boolean mdataactivityin, mdataactivityout;
    private isystemuistatusbarext mphonestateext;
    /// @ }

    public phonestate(int subid, context context) {
        //加载 mobile_signal_group_ext 布局文件
        viewgroup root = (viewgroup) layoutinflater.from(context)
                .inflate(r.layout.mobile_signal_group_ext, null);

        /// m: add data group for plugin feature. @ {
        mphonestateext = opsystemuicustomizationfactorybase.getopfactory(context)
                            .makesystemuistatusbar(context);
        mphonestateext.addcustomizedview(subid, context, root);
        /// @ }

        setviews(root);
        msubid = subid;
    }

    //控件初始化
    public void setviews(viewgroup root) {
        mmobilegroup    = root;
        mmobile         = root.findviewbyid(r.id.mobile_signal);
        mmobiledark     = root.findviewbyid(r.id.mobile_signal_dark);
        mmobiletype     = root.findviewbyid(r.id.mobile_type);
       ///m: add for [network type and volte on statusbar]
        mnetworktype    = (imageview) root.findviewbyid(r.id.network_type);
        mvoltetype      = (imageview) root.findviewbyid(r.id.volte_indicator_ext);
        mmobileroaming  = root.findviewbyid(r.id.mobile_roaming);
        mmobileactivityin = root.findviewbyid(r.id.mobile_in);
        mmobileactivityout = root.findviewbyid(r.id.mobile_out);

        // todo: remove the 2 instances because now the drawable can handle darkness.
        mmobile.setimagedrawable(new signaldrawable(mmobile.getcontext()));
        signaldrawable drawable = new signaldrawable(mmobiledark.getcontext());
        drawable.setdarkintensity(1);
        mmobiledark.setimagedrawable(drawable);
    }

    public boolean apply(boolean issecondaryicon) {
        log.e(tag, "apply()  mmobilevisible = " + mmobilevisible
                            + ", misairplanemode = " + misairplanemode
                            + ", miswfcenable = " + miswfcenable
                            + ", miswfccase = " + miswfccase
                            + ", mvolteicon = " + mvolteicon);
        if (mmobilevisible && !misairplanemode) {
             log.e(tag, "apply() into this code 1.. mmobilestrengthid=="+mmobilestrengthid);
            //设置信号格数
            if (mlastmobilestrengthid != mmobilestrengthid) {
                mmobile.getdrawable().setlevel(mmobilestrengthid);
                mmobiledark.getdrawable().setlevel(mmobilestrengthid);
                
                mlastmobilestrengthid = mmobilestrengthid;
            }
            
            //设置流量是否打开
            if (mlastmobiletypeid != mmobiletypeid) {
                if (!mphonestateext.disablehostfunction()) {
                    mmobiletype.setimageresource(mmobiletypeid);
                }
                mlastmobiletypeid = mmobiletypeid;
            }

            mmobilegroup.setcontentdescription(mmobiletypedescription
                    + " " + mmobiledescription);
            mmobilegroup.setvisibility(view.visible);
            showviewinwfccase();
        } else {
            if (misairplanemode && (miswfcenable && mvolteicon != 0)) {
                 log.e(tag, "apply() into this code 2..");
                /// m:bug fix for show vowifi icon in flight mode
                mmobilegroup.setvisibility(view.visible);
                hideviewinwfccase();
            } else {
                 log.e(tag, "apply() into this code 3..");
                if (debug) {
                    log.d(tag, "setvisibility as gone, this = " + this
                            + ", mmobilevisible = " + mmobilevisible
                            + ", misairplanemode = " + misairplanemode
                            + ", miswfcenable = " + miswfcenable
                            + ", mvolteicon = " + mvolteicon);
                }
                mmobilegroup.setvisibility(view.gone);
            }
        }

        /// m: set all added or customised view. @ {
        //更新网络类型和volte图标
        setcustomizeviewproperty();
        /// @ }

        // when this isn't next to wifi, give it some extra padding between the signals.
        mmobilegroup.setpaddingrelative(issecondaryicon ? msecondarytelephonypadding : 0,
                0, 0, 0);
        mmobile.setpaddingrelative(
                mismobiletypeiconwide ? mwidetypeiconstartpadding : mmobiledataiconstartpadding,
                0, 0, 0);
        mmobiledark.setpaddingrelative(
                mismobiletypeiconwide ? mwidetypeiconstartpadding : mmobiledataiconstartpadding,
                0, 0, 0);

        if (true) log.d(tag, string.format("mobile: %s sig=%d typ=%d",
                    (mmobilevisible ? "visible" : "gone"), mmobilestrengthid, mmobiletypeid));


         log.e(tag, "mactivityin="+mactivityin+" mactivityout="+mactivityout);   
        if(!miswfccase) {
            //更新流量是否打开可见性
            mmobiletype.setvisibility(mmobiletypeid != 0 ? view.visible :view.gone);
            //漫游图标
            mmobileroaming.setvisibility(mroaming ? view.visible : view.gone);
            //流量上下行图标
            mmobileactivityin.setvisibility(mactivityin ? view.visible : view.gone);
            mmobileactivityout.setvisibility(mactivityout ? view.visible : view.gone);
        }

        /// m: add for support plugin featurs. @ {
        //可通过op01/2/3等加载systemui,从6.0延伸来的
        setcustomizedopviews();
        /// @ }

        return mmobilevisible;
    }

 ......
}

phonestate 中的成员变量较多,我在代码里都已经加了注释了,就不细说了。加载的布局文件为

vendor\mediatek\proprietary\packages\apps\systemui\res_ext\layout\mobile_signal_group_ext.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- support [network type on statusbar] the layout to wrap original
mobile_signal_group and add image view for show network type -->
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    >
    <imageview
        android:id="@+id/volte_indicator_ext"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        />
    <imageview
        android:id="@+id/network_type"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:visibility="gone"
        /> 
    <include layout="@layout/mobile_signal_group"/>
  
</linearlayout>

布局文件中对应了 volte 和当前网络类型大图标, 再包含了 mobile_signal_group,里面就是上面提到的各种控件

vendor\mediatek\proprietary\packages\apps\systemui\res\layout\mobile_signal_group.xml

3、state.apply(anymobilevisible) 调用流程

通过上面的分析可以总结下 信号栏的调用流程

networkcontrollerimpl.java 中注册了sim卡状态改变广播(action_sim_state_changed),当收

到广播通知后调用到 notifyalllisteners()

通知所有的监听,mobilesignalcontroller、mwifisignalcontroller、

methernetsignalcontroller,分别对应手机信号显示、wifi信号、网卡显示通知

我们看到 mobilesignalcontroller 中的回调 notifylisteners(signalcallback callback),

在进行一系列的赋值操作后,最终回调到

signalclusterview 中的 setmobiledataindicators(),给 phonestate 的成员变量赋值,

最后通过 apply()进行更新

4、phonestate创建

通过刚刚对 phonestate 类的介绍,发现通过构造方法 phonestate(int subid, context context) 可获取 phonestate 对象

搜索当前文件找到在 inflatephonestate(int subid) 中实例化 phonestate

@override
public void setsubs(list<subscriptioninfo> subs) {
    if (debug) {
        log.d(tag, "setsubs, this = " + this + ", size = " + subs.size()
            + ", subs = " + subs);
    }
    if (hascorrectsubs(subs)) {//判断sim卡是否改变
        if (debug) {
            log.d(tag, "setsubs, hascorrectsubs and return");
        }
        return;
    }
    mphonestates.clear();
    if (mmobilesignalgroup != null) {
        mmobilesignalgroup.removeallviews();//移除手机信号组中的所有view 
    }
    final int n = subs.size();//sim卡数量
    log.d(tag, "setsubs-clear subsize:" + subs.size() + "mstes" + mphonestates + ":" + this);
    for (int i = 0; i < n; i++) {
        inflatephonestate(subs.get(i).getsubscriptionid());
    }
    if (isattachedtowindow()) {
        applyicontint();//图标根据背景色动态变化
    }
}

private phonestate inflatephonestate(int subid) {
    phonestate state = new phonestate(subid, mcontext);
    if (mmobilesignalgroup != null) {
        mmobilesignalgroup.addview(state.mmobilegroup);//添加view到手机信号组中
    }
    log.d(tag, "inflatephonestate add subid:" + subid + ",state" + state + ":" + this);
    mphonestates.add(state);//存储phonestate 对象,方便快速修改状态
    return state;
}

当sim卡插入识别后将回调 setsubs(),先判断sim卡是否改变,有效则移除 mobilegroup 中已添加

所有view,遍历sim卡数量,通过 subid 创建 phonestate,并将对应的view添加到 mobilegroup 中。

然后将 phonestate对象存储到集合中,方便快速修改状态

这里简单说下 subid subid对应卡,slotid对应卡槽

slotid或者phoneid是指卡槽,双卡机器的卡槽1值为0,卡槽2值为1,依次类推

subid的值从1开始,每插入一个新卡,subid的值就会加1。

插入双卡后数据库中就会有subid值为1和2的两个数据条目,

拔卡插卡交换卡槽后,数据库并不会增加新项,只有插入一张新的sim卡才会增加一条id为3的数据条目

详细的介绍请看这篇 subid、slotid

5、sim卡插入后更新图标流程

phonestate 创建成功了并存到集合中,当收到 setmobiledataindicators()回调后

给 phonestate 成员变量赋值,赋值结束通过apply()更新

还记得上面说过的 apply() 中更新sim卡图标的逻辑吧,

遍历 mphonestates 集合,调用phonestate的apply()将成员变量值设置给对应的控件

int firstmobiletypeid = 0;
    for (phonestate state : mphonestates) {
        if (state.apply(anymobilevisible)) {
            if (!anymobilevisible) {
                firstmobiletypeid = state.mmobiletypeid;
                anymobilevisible = true;
            }
        }
    }

那么 setmobiledataindicators() 是从哪里回调过来的呢?

分析找到 mobilesignalcontroller 中的 notifylisteners()

vendor\mediatek\proprietary\packages\apps\systemui\src\com\android\systemui\statusbar\policy\mobilesignalcontroller.java

@override
public void notifylisteners(signalcallback callback) {
    //获取资源id组
    mobileicongroup icons = geticons();

    string contentdescription = getstringifexists(getcontentdescription());
    string datacontentdescription = getstringifexists(icons.mdatacontentdescription);
    //移动数据是否开启
    final boolean datadisabled = mcurrentstate.icongroup == telephonyicons.data_disabled
            && mcurrentstate.usersetup;

    /// m: customize the signal strength icon id. @ {
    //当前手机信号格数资源id
    int iconid = getcurrenticonid();
    //用户可自定义的资源id,该方法将目前的iconid原值赋给了iconid,如需定制可在此修改
    iconid = mstatusbarext.getcustomizesignalstrengthicon(
                msubscriptioninfo.getsubscriptionid(),
                iconid,
                msignalstrength,
                mdatanettype,
                mservicestate);
    /// @ }

    // show icon in qs when we are connected or data is disabled.
    //是否显示移动数据图标
    boolean showdataicon = mcurrentstate.dataconnected || datadisabled;
    //是否显示 mobilegroup、信号格数、sim卡信息描述
    iconstate statusicon = new iconstate(mcurrentstate.enabled && !mcurrentstate.airplanemode,
            iconid, contentdescription);

    int qstypeicon = 0;
    iconstate qsicon = null;
    string description = null;
    // only send data sim callbacks to qs.
    if (mcurrentstate.datasim) {
        qstypeicon = showdataicon ? icons.mqsdatatype : 0;
        qsicon = new iconstate(mcurrentstate.enabled
                && !mcurrentstate.isemergency, getqscurrenticonid(), contentdescription);
        //状态栏显示只能拨打紧急电话或当前的网络类型
        description = mcurrentstate.isemergency ? null : mcurrentstate.networkname;
    }
    //数据下行
    boolean activityin = mcurrentstate.dataconnected
            && !mcurrentstate.carriernetworkchangemode
            && mcurrentstate.activityin;
    //数据上行
    boolean activityout = mcurrentstate.dataconnected
            && !mcurrentstate.carriernetworkchangemode
            && mcurrentstate.activityout;
    showdataicon &= mcurrentstate.isdefault || datadisabled;
    //移动数据类型资源id,关闭是x,打开是小的网络类型4g/3g/2g
    int typeicon = showdataicon ? icons.mdatatype : 0;

    /// m: add for lwa.
    typeicon = mcurrentstate.lwaregstate == networktypeutils.lwa_state_conncted
            && showdataicon ? networktypeutils.lwa_icon : typeicon;
    /** m: support [network type on statusbar], change the implement methods.
      * get the network icon base on service state.
      * add one more parameter for network type.
      * @ { **/
    //当前网络类型资源id
    int networkicon = mcurrentstate.networkicon;
    /// m: support volte icon.bug fix when airplane mode is on go to hide volte icon
    //volte资源id
    int volteicon = mcurrentstate.airplanemode && !isimsoverwfc()
            ? 0 : mcurrentstate.volteicon;

    /// m: when data disabled, common show data icon as x, but op do not need show it @ {
    mstatusbarext.isdatadisabled(msubscriptioninfo.getsubscriptionid(), datadisabled);
    /// @ }

    /// m: customize the data type icon id. @ {
    //可自定义移动数据类型资源id(比如常见的上下箭头)
    typeicon = mstatusbarext.getdatatypeicon(
                    msubscriptioninfo.getsubscriptionid(),
                    typeicon,
                    mdatanettype,
                    mcurrentstate.dataconnected ? telephonymanager.data_connected :
                        telephonymanager.data_disconnected,
                    mservicestate);
    /// @ }
    /// m: customize the network type icon id. @ {
    //可自定义网络类型资源id
    networkicon = mstatusbarext.getnetworktypeicon(
                    msubscriptioninfo.getsubscriptionid(),
                    networkicon,
                    mdatanettype,
                    mservicestate);

    callback.setmobiledataindicators(statusicon, qsicon, typeicon, networkicon, volteicon,
            qstypeicon,activityin, activityout, datacontentdescription, description,
             icons.miswide, msubscriptioninfo.getsubscriptionid(), mcurrentstate.roaming);

    /// m: update plmn label @{
    mnetworkcontroller.refreshplmncarrierlabel();
    /// @}
}

这个方法比较重要,上面写了简单的注释,接下来我们会详细看下每个资源id都是如何获取的?

在这之前我们先介绍下几个重要的 bean 类

vendor\mediatek\proprietary\packages\apps\systemui\src\com\android\systemui\statusbar\policy\signalcontroller.java

state

static class state {
    boolean connected;
    boolean enabled;
    boolean activityin;
    boolean activityout;
    int level;
    icongroup icongroup;
    int inetcondition;
    int rssi;
    ...
}

icongroup

static class icongroup {
    final int[][] msbicons;
    final int[][] mqsicons;
    final int[] mcontentdesc;
    final int msbnullstate;
    final int mqsnullstate;
    final int msbdiscstate;
    final int mqsdiscstate;
    final int mdisccontentdesc;
    // for logging.
    final string mname;
    ....
}

vendor\mediatek\proprietary\packages\apps\systemui\src\com\android\systemui\statusbar\policy\mobilesignalcontroller.java

mobilestate

static class mobilestate extends signalcontroller.state {
    string networkname;//当前网络类型
    string networknamedata;//移动数据网络类型
    boolean datasim;
    boolean dataconnected;//数据是否连接
    boolean isemergency;//是否是紧急电话模式
    boolean airplanemode;//是否是飞行模式
    boolean carriernetworkchangemode;//sim卡网络类型是否改变
    boolean isdefault;
    boolean usersetup;//是否是用户操作
    boolean roaming;//是否漫游

    /// m: add for 4g+w
    int lwaregstate = networktypeutils.lwa_state_unknown;
    /// m: for network type big icon.
    int networkicon;//网络类型大图标资源id
    /// m: add for data network type.
    int datanettype;//移动数据网络类型
    /// m: add for op network tower type.
    int customizedstate;//自定义状态
    /// m: add for op signal strength tower icon.
    int customizedsignalstrengthicon;//自定义信号格资源id
    /// m: add for volte @{
    int imsregstate = servicestate.state_power_off;
    int imscap;
    int volteicon;//volte资源id

    ......  
}

mobileicongroup

static class mobileicongroup extends signalcontroller.icongroup {
    final int mdatacontentdescription; // mcontentdescriptiondatatype
    final int mdatatype;//移动数据网络类型资源id
    final boolean miswide;
    final int mqsdatatype;//下拉快捷访问资源

    ...     
}

好了重要的bean类介绍完了,接下来又要说一个重要的方法了 updatetelephony()

还是在 mobilesignalcontroller.java 中

private final void updatetelephony() {
    if (debug && featureoptions.log_enable) {
        log.d(mtag, "updatetelephonysignalstrength: hasservice=" + hasservice()
                + " ss=" + msignalstrength);
    }
    //连接状态,是否在服务中
    mcurrentstate.connected = hasservice() && msignalstrength != null;
    handleiwlannetwork();
    if (mcurrentstate.connected) {
        //sim 卡信号格数级别 0~4格
        if (!msignalstrength.isgsm() && mconfig.alwaysshowcdmarssi) {
            mcurrentstate.level = msignalstrength.getcdmalevel();
        } else {
            mcurrentstate.level = msignalstrength.getlevel();
        }
        /// m: customize the signal strength level. @ {
        //客户可自定义
        mcurrentstate.level = mstatusbarext.getcustomizesignalstrengthlevel(
                mcurrentstate.level, msignalstrength, mservicestate);
        /// @ }
    }
    //当前网络类型获取对应的图标组
    if (mnetworktoiconlookup.indexofkey(mdatanettype) >= 0) {
        mcurrentstate.icongroup = mnetworktoiconlookup.get(mdatanettype);
    } else {
        mcurrentstate.icongroup = mdefaulticons;
    }
    /// m: add for data network type.
    //数据网络类型
    mcurrentstate.datanettype = mdatanettype;
    //数据状态
    mcurrentstate.dataconnected = mcurrentstate.connected
            && mdatastate == telephonymanager.data_connected;
    /// m: add for op network tower type.
    mcurrentstate.customizedstate = mstatusbarext.getcustomizecsstate(mservicestate,
            mcurrentstate.customizedstate);
    /// m: add for op signal strength tower icon.
    mcurrentstate.customizedsignalstrengthicon = mstatusbarext.getcustomizesignalstrengthicon(
            msubscriptioninfo.getsubscriptionid(),
            mcurrentstate.customizedsignalstrengthicon,
            msignalstrength,
            mdatanettype,
            mservicestate);

    mcurrentstate.roaming = isroaming();
    if (iscarriernetworkchangeactive()) {
        mcurrentstate.icongroup = telephonyicons.carrier_network_change;
    } else if (isdatadisabled()) {//数据未打开,对应x
        mcurrentstate.icongroup = telephonyicons.data_disabled;
    }
    if (isemergencyonly() != mcurrentstate.isemergency) {
        mcurrentstate.isemergency = isemergencyonly();
        mnetworkcontroller.recalculateemergency();
    }
    // fill in the network name if we think we have it.
    //当前网络运营商
    if (mcurrentstate.networkname == mnetworknamedefault && mservicestate != null
            && !textutils.isempty(mservicestate.getoperatoralphashort())) {
        mcurrentstate.networkname = mservicestate.getoperatoralphashort();
    }
    /// m: for network type big icon. 网络类型大图标
    mcurrentstate.networkicon =
        networktypeutils.getnetworktypeicon(mservicestate, mconfig, hasservice());
    /// m: for volte type icon. volte图标
    mcurrentstate.volteicon = getvolteicon();
    
    //通知更新,最终回调到notifylisteners()中
    notifylistenersifnecessary();
}

基本上获取资源id的方法都在 updatetelephony()中了,那么都在那里调用了 updatetelephony()?

mobilesignalcontroller中构造方法初始化了 mobilephonestatelistener 分别监听了

mphone.listen(mphonestatelistener,
phonestatelistener.listen_service_state//服务状态改变,可用、不可用
        | phonestatelistener.listen_signal_strengths//信号强度改变,用于获取dbm、asu
        | phonestatelistener.listen_call_state//电话状态改变,空闲、来电、通话
        | phonestatelistener.listen_data_connection_state//数据网络连接状态,网络断开、正在连接中、已连接上
        | phonestatelistener.listen_data_activity//数据上下行状态
        | phonestatelistener.listen_carrier_network_change);//网络状态发送改变

class mobilephonestatelistener extends phonestatelistener {
    public mobilephonestatelistener(int subid, looper looper) {
        super(subid, looper);
    }

    @override
    public void onsignalstrengthschanged(signalstrength signalstrength) {
        ...
        updatetelephony();
    }

    @override
    public void onservicestatechanged(servicestate state) {
        ...
        updatetelephony();
    }

    @override
    public void ondataconnectionstatechanged(int state, int networktype) {
        ...
        updatetelephony();
    }

    @override
    public void ondataactivity(int direction) {
        ...
        setactivity(direction);
    }

    @override
    public void oncarriernetworkchange(boolean active) {
       ...
        updatetelephony();
    }

    /// m: add for plugin feature. @{
    @override
    public void oncallstatechanged(int state, string incomingnumber) {
        ...
        updatetelephony();
       
    }
    /// @}
};

listen_signal_strengths、listen_call_state、listen_carrier_network_change

这三个监听应该是我们平常用较多的,好了说了这么久,接下来重要看获取资源id的具体方法了

5.1、vlote资源id

mcurrentstate.volteicon = getvolteicon();

private int getvolteicon() {
    int icon = 0;
    if (isimsoverwfc()) {
        boolean needshowwfcsysicon = mstatusbarext.needshowwfcicon();
        if (needshowwfcsysicon) {
            icon = networktypeutils.wfc_icon;
        }
    } else if (isimsovervoice() && isltenetwork()) {
        if (mcurrentstate.imsregstate == servicestate.state_in_service) {
            //volte可用
            icon = networktypeutils.volte_icon;
        } else if(featureoptions.mtk_ct_mixed_volte_support &&
            simhelper.issecondarycsimformixedvolte(msubscriptioninfo.getsubscriptionid()) &&
            mcurrentstate.imsregstate == servicestate.state_out_of_service) {
            if (debug) {
                log.d(mtag, "set dis volte icon");
            }//volte不可用
            icon = networktypeutils.volte_dis_icon;
        }
    }
    /// m: add for disconnected volte feature. @{
    mstatusbarext.setimsreginfo(msubscriptioninfo.getsubscriptionid(),
            mcurrentstate.imsregstate, isimsoverwfc(), isimsovervoice());
    /// @}
    return icon;
}

vendor\mediatek\proprietary\packages\apps\systemui\src\com\mediatek\systemui\statusbar\networktype\networktypeutils.java

public static final int volte_icon = r.drawable.stat_sys_volte;

在这里插入图片描述

5.2、网络类型大图标资源id

vendor\mediatek\proprietary\packages\apps\systemui\src\com\mediatek\systemui\statusbar\networktype\networktypeutils.java

mcurrentstate.networkicon =
        networktypeutils.getnetworktypeicon(mservicestate, mconfig, hasservice());

public static int getnetworktypeicon(servicestate servicestate, config config,
        boolean hasservice) {
    if (!hasservice) {
        // not in service, no network type. 未注册成功,比如废卡、停机卡
        return 0;
    }
    //通过 servicestate 获取当前注册的网络类型
    int tempnetworktype = getnetworktype(servicestate);

    integer iconid = snetworktypeicons.get(tempnetworktype);
    if (iconid == null) {
        iconid = tempnetworktype == telephonymanager.network_type_unknown ? 0 :
                 config.showatleast3g ? r.drawable.stat_sys_network_type_3g :
                                        r.drawable.stat_sys_network_type_g;
    }
    return iconid.intvalue();
}

private static int getnetworktype(servicestate servicestate) {
    int type = telephonymanager.network_type_unknown;
    if (servicestate != null) {
        type = servicestate.getdatanetworktype() != telephonymanager.network_type_unknown ?
                servicestate.getdatanetworktype() : servicestate.getvoicenetworktype();
    }
    return type;
}

//网络类型-资源id    4g/3g/2g/e/1x
static final map<integer, integer> snetworktypeicons = new hashmap<integer, integer>() {
    {
        // for cdma 3g
        put(telephonymanager.network_type_evdo_0, r.drawable.stat_sys_network_type_3g);
        put(telephonymanager.network_type_evdo_a, r.drawable.stat_sys_network_type_3g);
        put(telephonymanager.network_type_evdo_b, r.drawable.stat_sys_network_type_3g);
        put(telephonymanager.network_type_ehrpd, r.drawable.stat_sys_network_type_3g);
        // for cdma 1x
        put(telephonymanager.network_type_cdma, r.drawable.stat_sys_network_type_1x);
        put(telephonymanager.network_type_1xrtt, r.drawable.stat_sys_network_type_1x);
        // edge
        put(telephonymanager.network_type_edge, r.drawable.stat_sys_network_type_e);
        // 3g
        put(telephonymanager.network_type_umts, r.drawable.stat_sys_network_type_3g);
        // for 4g
        put(telephonymanager.network_type_lte, r.drawable.stat_sys_network_type_4g);
        // 3g
        put(telephonymanager.network_type_hsdpa, r.drawable.stat_sys_network_type_3g);
        put(telephonymanager.network_type_hsupa, r.drawable.stat_sys_network_type_3g);
        put(telephonymanager.network_type_hspa, r.drawable.stat_sys_network_type_3g);
        put(telephonymanager.network_type_hspap, r.drawable.stat_sys_network_type_3g);
        put(telephonymanager.network_type_iwlan, 0);
    }
};

在这里插入图片描述

5.3、移动数据类型资源id

//mnetworktoiconlookup 和上面的网络类型 map有点类似,键都是网络类型,
//不同的是,这次的key是上面介绍过的 mobileicongroup
if (mnetworktoiconlookup.indexofkey(mdatanettype) >= 0) {
    mcurrentstate.icongroup = mnetworktoiconlookup.get(mdatanettype);
} else {
    mcurrentstate.icongroup = mdefaulticons;
}

if (iscarriernetworkchangeactive()) {
    mcurrentstate.icongroup = telephonyicons.carrier_network_change;
} else if (isdatadisabled()) {
    mcurrentstate.icongroup = telephonyicons.data_disabled;
}

vendor\mediatek\proprietary\packages\apps\systemui\src\com\android\systemui\statusbar\policy\telephonyicons.java

class telephonyicons {
    //***** data connection icons
    
    //状态栏快捷访问,其实和下面的差多不
    static final int qs_data_g = r.drawable.ic_qs_signal_g;
    static final int qs_data_3g = r.drawable.ic_qs_signal_3g;
    static final int qs_data_e = r.drawable.ic_qs_signal_e;
    static final int qs_data_h = r.drawable.ic_qs_signal_h;
    static final int qs_data_1x = r.drawable.ic_qs_signal_1x;
    static final int qs_data_4g = r.drawable.ic_qs_signal_4g;
    static final int qs_data_4g_plus = r.drawable.ic_qs_signal_4g_plus;
    static final int qs_data_lte = r.drawable.ic_qs_signal_lte;
    static final int qs_data_lte_plus = r.drawable.ic_qs_signal_lte_plus;

    static final int flight_mode_icon = r.drawable.stat_sys_airplane_mode;
    
    //此处的图标为小图标,网络类型
    static final int icon_lte = r.drawable.stat_sys_data_fully_connected_lte;
    static final int icon_lte_plus = r.drawable.stat_sys_data_fully_connected_lte_plus;
    static final int icon_g = r.drawable.stat_sys_data_fully_connected_g;
    static final int icon_e = r.drawable.stat_sys_data_fully_connected_e;
    static final int icon_h = r.drawable.stat_sys_data_fully_connected_h;
    static final int icon_3g = r.drawable.stat_sys_data_fully_connected_3g;
    static final int icon_4g = r.drawable.stat_sys_data_fully_connected_4g;
    static final int icon_4g_plus = r.drawable.stat_sys_data_fully_connected_4g_plus;
    static final int icon_1x = r.drawable.stat_sys_data_fully_connected_1x;

    //流量未打开
    static final int icon_data_disabled = r.drawable.stat_sys_data_disabled;
    static final int qs_icon_data_disabled = r.drawable.ic_qs_data_disabled;

    ...

    static final mobileicongroup data_disabled = new mobileicongroup(
        "datadisabled",
        null,
        null,
        accessibilitycontentdescriptions.phone_signal_strength,
        0, 0,
        0,
        0,
        accessibilitycontentdescriptions.phone_signal_strength[0],
        r.string.accessibility_cell_data_off,
        telephonyicons.icon_data_disabled,//这个值对应的就是 移动数据类型资源id
        false,
        telephonyicons.qs_icon_data_disabled
        );

}

mobileicongroup 的倒数第三个参数就是 移动数据类型资源id

在这里插入图片描述

5.4、信号格数资源id

if (mcurrentstate.connected) {
        if (!msignalstrength.isgsm() && mconfig.alwaysshowcdmarssi) {
            mcurrentstate.level = msignalstrength.getcdmalevel();
        } else {
            mcurrentstate.level = msignalstrength.getlevel();
        }
        /// m: customize the signal strength level. @ {
        mcurrentstate.level = mstatusbarext.getcustomizesignalstrengthlevel(
                mcurrentstate.level, msignalstrength, mservicestate);
        /// @ }
 }

信号格数对应的是 signaldrawable,通过 setlevel()来控制显示几格,其实以上的大部分资源id都是

通过 vector 标签绘制而来的,里面都是一堆 path,开始看可能会觉得很迷糊,可以把xml文件拷贝到

as中进行预览,再学上一些基础语法就可对简单的图形进行自定义修改。比方说6.0的信号格数是通过

vector 绘制的,格与格之间是有间隔,而8.1是通过 signaldrawable绘制,是一个填满的三角形

在这里插入图片描述
修改前样式
在这里插入图片描述
修改后样式

来看下 msignalstrength.getlevel() 方法

frameworks/base/telephony/java/android/telephony/signalstrength.java

    public int getlevel() {
    int level = 0;
    if (isgsm) { //移动或联通卡
        level = getltelevel(); //首先获取4g信号格
        if (level == signal_strength_none_or_unknown) { //未获取到
            level = gettdscdmalevel(); //获取移动或联通的3g信号格
            if (level == signal_strength_none_or_unknown) {//仍然未获取
                level = getgsmlevel(); //获取移动或联通的2g信号格
            }
        }
    } else {//电信
        int cdmalevel = getcdmalevel(); //获取电信2g信号格
        int evdolevel = getevdolevel(); //获取电信3g信号格
        if (evdolevel == signal_strength_none_or_unknown) {
            /* we don't know evdo, use cdma */
            level = cdmalevel;
        } else if (cdmalevel == signal_strength_none_or_unknown) {
            /* we don't know cdma, use evdo */
            level = evdolevel;
        } else {
            /* we know both, use the lowest level */
            level = cdmalevel < evdolevel ? cdmalevel : evdolevel;
        }
    }
    if (dbg) log("getlevel=" + level);
    return level;
}

在此介绍下手机是几模的配置:gsm是移动和联通公用的band;lte从编码方式上分为tdd和fdd,从频段上分有各种不同的band

移动:gsm、tdscdma、lte(tdd)

联通:gsm、wcdma、lte(fdd)

电信:cdma、evdo、lte(fdd)

因此如果手机支持gsm、wcdma、tdscdma、tdd-lte、fdd-lte 这是五模;加上 cdma、evdo 就是七模

5.5、漫游资源id r.drawable.stat_sys_roaming

大写的r

三、总结

信号栏的定制还是很容易的,只要理清楚了控件和对应的回调逻辑,加上日志打印,就能搞定你想要的效果。

四、相关资源

这里附上我定制使用的drawable文件

在这里插入图片描述

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网