Android8.1 MTK平台 SystemUI源码分析之 网络信号栏显示刷新
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文件