以上就是整个界面显示的xml加载流程,后面会逐个源码贴出来。
加载system_icons.xml
这是父类是一个linearLayout 水平布局,从代码中不仅仅加载了信号图标区域(signal_cluster_view)还加载了电量显示图标(BatterMeterView),从现象看到与代码是一致的,电量图标一直在信号图标的右侧。
这段代码加载了一个自定义的SignalClusterView,SignalClusterView是一个继承LinearLayout的ViewGroup,这个xml布局不仅仅包含了手机信号图标还有wifi以及飞行模式等。有朋友会问,从这个xml文件中我没看到mobile_signal_group_ext.xml的任何信息,那么mobile_signal_group_ext.xml是怎么被添加到手机信号图标树中呢?答案是通过代码的方式加载,在SignalClusterView.java 中有这么一段代码:
public PhoneState(int subId, Context context) {
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;
}
这两个xml布局都比较简单,其中对应的具体图标如下图
至此结构树的分析到此。
其实从网上能收到很多资料关于手机信号图标的信息,NetworkControllerImpl.java是手机信号处理逻辑类,这个类继承了BroadcastReceiver.java ,因此手机信号变化是监听一些信号广播来做修改的。之前提到的SignalClusterView.java则是信号显示类。那么这两个类是如何联系在一起的呢?答案是通过NetworkControllerImpl.SignalCallback,NetworkControllerImpl.SignalCallback是一个接口。
SignalClusterView.java继承NetworkControllerImpl.SignalCallback,并且向NetworkControllerImpl.java注册接口信息,NetworkControllerImpl.java根据信号广播回调NetworkControllerImpl.SignalCallback。
public class SignalClusterView extends LinearLayout implements NetworkControllerImpl.SignalCallback,
SecurityController.SecurityControllerCallback, Tunable,
DarkReceiver {
public void setForceBlockWifi() {
mForceBlockWifi = true;
mBlockWifi = true;
if (isAttachedToWindow()) {
// Re-register to get new callbacks.
mNetworkController.removeCallback(this);
mNetworkController.addCallback(this);
}
}
mNetWorkController 就是NetworkControllerImpl.Java实例,这个实例在SystemUI体系中被统一保存在Dependency.java中,有兴趣的朋友可以自己阅读相关的源码。下图是两者的关系。
后面我们捋一捋信号变化的逻辑部分,NetworkControllerImpl.java既然是一个BroadcastReceiver,那肯定会涉及到广播的注册和监听。
private void registerListeners() {
Log.e(TAG, "registerListeners-----------------------------");
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
StringBuilder st=new StringBuilder();
mobileSignalController.mCurrentState.toString(st);
Log.e(TAG, "registerListeners------- mNetworkNameDefault"+mobileSignalController.mNetworkNameDefault+" i ="+i+" st="+st.toString());
mobileSignalController.registerListener();
}
if (mSubscriptionListener == null) {
mSubscriptionListener = new SubListener();
}
mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener);
// broadcasts
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
filter.addAction(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
/// M: Add mtk
addCustomizedAction(filter);
mContext.registerReceiver(this, filter, null, mReceiverHandler);
mListening = true;
updateMobileControllers();
}
public void onReceive(Context context, Intent intent) {
if (CHATTY) {
Log.d(TAG, "onReceive: intent=" + intent);
}
final String action = intent.getAction();
if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) ||
action.equals(ConnectivityManager.INET_CONDITION_ACTION)) {
updateConnectivity();
}
//飞行模式变化
else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
refreshLocale();
updateAirplaneMode(false);
} else if (action.equals(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED)) {
// We are using different subs now, we might be able to make calls.
recalculateEmergency();
} else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
// Notify every MobileSignalController so they can know whether they are the
// data sim or not.
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController controller = mMobileSignalControllers.valueAt(i);
controller.handleBroadcast(intent);
}
} else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
// Might have different subscriptions now.
updateMobileControllers();
/// M: Support "subinfo record update". @{
} else if (action.equals(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED)) {
/// M: update plmn label @{
refreshPlmnCarrierLabel();
/// @}
updateMobileControllersEx(intent);
/// @}
} else if (action.equals(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED)) {
mLastServiceState = ServiceState.newFromBundle(intent.getExtras());
/// M:[ALPS02809725]Always save ecc state not only no sim
/// M: [ALPS02614114] Check all phone's emergency state when no sims @{
if (mLastServiceState != null) {
int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY,
PhoneConstants.SIM_ID_1);
mEmergencyPhone[phoneId] = mLastServiceState.isEmergencyOnly();
if (DEBUG) {
Log.d(TAG, "Service State changed...phoneId: " + phoneId
+ " ,isEmergencyOnly: " + mEmergencyPhone[phoneId]);
}
if (mMobileSignalControllers.size() == 0) {
// If none of the subscriptions are active, we might need to recalculate
// emergency state.
recalculateEmergency();
}
// @}
}
} else if (action.equals(NetworkTypeUtils.LWA_STATE_CHANGE_ACTION)) {
/// M: Add for 4G+W
handleLwaAction(intent);
}
else {
int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
if (SubscriptionManager.isValidSubscriptionId(subId)) {
if (mMobileSignalControllers.indexOfKey(subId) >= 0) {
mMobileSignalControllers.get(subId).handleBroadcast(intent);
} else {
// Can't find this subscription... We must be out of date.
updateMobileControllers();
}
} else {
// No sub id, must be for the wifi.
mWifiSignalController.handleBroadcast(intent);
}
}
}
注释:
public void notifyListeners(SignalCallback callback) {
MobileIconGroup icons = getIcons();
Log.d(mTag, "mCurrentState.inetCondition " + mCurrentState.inetCondition + "mCurrentState.level"
+ mCurrentState.level);
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. @ {
int iconId = getCurrentIconId();
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;//modify xiao
IconState statusIcon = new IconState(mCurrentState.enabled && !mCurrentState.airplaneMode,
iconId, contentDescription);
Log.d(mTag, "notifyListeners: contentDescription=" + contentDescription+" dataContentDescription=" + dataContentDescription+
" dataDisabled=" + dataDisabled+" iconId=" + iconId+" showDataIcon=" + showDataIcon
+" mCurrentState.dataSim:"+mCurrentState.dataSim);
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;
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.
* @ { **/
int networkIcon = mCurrentState.networkIcon;
/// M: Support volte icon.Bug fix when airplane mode is on go to hide volte icon
int volteIcon = mCurrentState.airplaneMode && !isWfcEnable()
? 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. @ {
typeIcon = mStatusBarExt.getDataTypeIcon(
mSubscriptionInfo.getSubscriptionId(),
typeIcon,
mDataNetType,
mCurrentState.dataConnected ? TelephonyManager.DATA_CONNECTED :
TelephonyManager.DATA_DISCONNECTED,
mServiceState);
/// @ }
/// M: Customize the network type icon 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();
/// @}
}
1. typeIcon就是我们之前在分析控制树中有一张图片所显示4G ,networkIcon 3G,qsIcon是三角形图标,最终是调用SignalDrawable,SignalDrawable的主要工作就是根据信号强弱来画三角形图标。 SignalStrength表示信号等级,是通过MobilePhoneStateListener接口得到的。
2. typeIcon保存和获取会用到一个类TelephonyIcons.java,这个类保存了所有要显示信号类型。MobileSignalController-->mapIconSets() :根据信号类型加载不同的图标,就是我们在有的时候看到是3G,有时候显示的是4G。根据信号类型是如何获取得到的呢?答案是MobilePhoneStateListener 接口中onServiceStateChanged方法有这么一段代码
if (state != null) {
mDataNetType = state.getDataNetworkType();
3. networkIcon 保存和获取会用到一个类NetworkTypeUtils.java,这个类保存了所有要显示的网络类型图标,根据不同的ServiceState 和config来加载对应的网络类型图标。而ServiceState是依据MobilePhoneStateListener接口获取,config是根据SystemUI下面的Config.xml来配置的。NetworkControllerImpl.Java会加载相关的配置。
public static int getNetworkTypeIcon(ServiceState serviceState, Config config,
public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
int networkType, int volteIcon, int qsType, boolean activityIn, boolean activityOut,
String typeContentDescription, String description, boolean isWide, int subId,
boolean roaming) {
PhoneState state = getState(subId);
if (state == null) {
return;
}
state.mMobileVisible = statusIcon.visible && !mBlockMobile;
state.mMobileStrengthId = statusIcon.icon;
state.mMobileTypeId = statusType;
state.mMobileDescription = statusIcon.contentDescription;
state.mMobileTypeDescription = typeContentDescription;
state.mIsMobileTypeIconWide = statusType != 0 && isWide;
/// M: for big network icon and volte icon.
state.mNetworkIcon = networkType;
state.mVolteIcon = volteIcon;
state.mRoaming = roaming;
state.mActivityIn = activityIn && mActivityEnabled;
state.mActivityOut = activityOut && mActivityEnabled;
/// M: Add for plugin features. @ {
state.mDataActivityIn = activityIn;
state.mDataActivityOut = activityOut;
/// @ }
Log.d(TAG, "setMobileDataIndicators description:"+description+" typeContentDescription="+typeContentDescription);
apply();
}
至此手机信号图标的控制加载流程讲述完毕,本文只是讲了大体的流程,其中详情,可以自行阅读。