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文件,CSDN有点坑啊,我想传免费的来着,非要给我设置5分,好吧,只好换平台改链接免费下载