原生设计中,移动数据图标只有在网络活动(下载/上传)时,才显示相应的小白色三角图标,如果没有网络活动则没有任何显示。需要在不活动时也显示灰色的三角图标。
系统导航栏中常见信号图标包括:SIM卡信号(移动数据图标)、WIFI。主要关注几个文件
网络监听控制 NetworkControllerImpl.java
信号变化控制 MobileSignalController.java WifiSignalController.java
图标显示view StatusBarMobileView.java StatusBarWifiView.java
一、增加移动网络非活动状态图标view
在原生基础上,增加一个默认灰色三角图标,启用移动数据时就显示在状态栏中。
//frameworks/base/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
... ...
<FrameLayout
android:id="@+id/inout_container"
android:layout_height="17dp"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical">
<!-- Add: 增加一层默认图标,使用移动数据时显示 -->
<ImageView
android:id="@+id/mobile_open"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/ic_activity_tran"
android:paddingEnd="2dp"
/>
<!-- Add -->
<ImageView
android:id="@+id/mobile_in"
android:layout_height="wrap_content"
... ...
二、移动网络状态判断
系统网络发生变化时,会通过广播的方式,通知 NetworkControllerImpl.java 中进行对应变更。
// NetworkControllerImpl.java
@Override
public void onReceive(Context context, Intent intent) {
if (CHATTY) {
Log.d(TAG, "onReceive: intent=" + intent);
}
final String action = intent.getAction();
switch (action) {
case ConnectivityManager.CONNECTIVITY_ACTION:
case ConnectivityManager.INET_CONDITION_ACTION:
updateConnectivity();
break;
case Intent.ACTION_AIRPLANE_MODE_CHANGED:
refreshLocale();
updateAirplaneMode(false);
break;
case TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED:
// We are using different subs now, we might be able to make calls.
recalculateEmergency();
break;
case TelephonyManager.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);
}
mConfig = Config.readConfig(mContext);
mReceiverHandler.post(this::handleConfigurationChanged);
break;
case Intent.ACTION_SIM_STATE_CHANGED:
// Avoid rebroadcast because SysUI is direct boot aware.
if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
break;
}
// Might have different subscriptions now.
updateMobileControllers();
break;
... ...
}
}
NetworkControllerImpl 中对各种网络的各种状态变化进行广播监听,执行相应操作。上面代码中几个Action是SIM相关的。虽然每个执行的方法不同,最后都是执行到
MobileSignalController.notifyListeners
这里,根据当前信号状态,对图标(IconState)状态进行刷新。原生设计中这里只有activityIn activityOut 标识网络活动,新增加非网络活动的移动数据状态。
//MobileSignalController.java
public void notifyListeners(SignalCallback callback) {
... ...
boolean activityIn = mCurrentState.dataConnected
&& !mCurrentState.carrierNetworkChangeMode
&& mCurrentState.activityIn;
boolean activityOut = mCurrentState.dataConnected
&& !mCurrentState.carrierNetworkChangeMode
&& mCurrentState.activityOut;
showDataIcon &= mCurrentState.isDefault || dataDisabled;
int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.mDataType : 0;
// Add: 增加是否是移动网络状态。isDefault 是否是移动数据模式 dataSim 是否是默认数据卡 mobileDataEnabled 移动数据是否开启状态
Log.d(mTag, "mCurrentState " + mCurrentState);
statusIcon.connType = (mCurrentState.isDefault && mCurrentState.dataSim && mCurrentState.mobileDataEnabled) ? 1 : 0;
// Add
callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml,
description, icons.mIsWide, mSubscriptionInfo.getSubscriptionId(),
mCurrentState.roaming);
}
再交由系统 StatusBarSignalPolicy.java 统一设置到具体的图标状态。对 MobileIconState 也增加一个默认移动数据连接标志 connType,同步更新记录状态。
//StatusBarSignalPolicy.java
@Override
public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
int qsType, boolean activityIn, boolean activityOut,
CharSequence typeContentDescription,
CharSequence typeContentDescriptionHtml, CharSequence description,
boolean isWide, int subId, boolean roaming) {
MobileIconState state = getState(subId);
if (state == null) {
return;
}
// Add: 同步 statusIcon 图标状态给具体的 MobileIconState
Log.i(TAG, "setMobileDataIndicators " + statusIcon.connType);
state.connType = statusIcon.connType;
// Add
// Visibility of the data type indicator changed
boolean typeChanged = statusType != state.typeId && (statusType == 0 || state.typeId == 0);
state.visible = statusIcon.visible && !mBlockMobile;
state.strengthId = statusIcon.icon;
state.typeId = statusType;
state.contentDescription = statusIcon.contentDescription;
state.typeContentDescription = typeContentDescription;
state.roaming = roaming;
state.activityIn = activityIn && mActivityEnabled;
state.activityOut = activityOut && mActivityEnabled;
// Always send a copy to maintain value type semantics
mIconController.setMobileIcons(mSlotMobile, MobileIconState.copyStates(mMobileStates));
}
... ...
public static class MobileIconState extends SignalIconState {
... ...
public CharSequence typeContentDescription;
public int connType;//Add: 使用移动数据
... ...
@Override
public boolean equals(Object o) {
... ...
return subId == that.subId &&
strengthId == that.strengthId &&
typeId == that.typeId &&
roaming == that.roaming &&
needsLeadingPadding == that.needsLeadingPadding &&
Objects.equals(typeContentDescription, that.typeContentDescription)&&
// Modify: 增加变量 connType
connType == that.connType;
}
@Override
public int hashCode() {
// Modify: 增加变量 connType
return Objects
.hash(super.hashCode(), subId, strengthId, typeId, roaming, needsLeadingPadding, typeContentDescription, connType);
}
... ...
public void copyTo(MobileIconState other) {
... ...
other.needsLeadingPadding = needsLeadingPadding;
other.typeContentDescription = typeContentDescription;
//Add: 增加变量 connType
other.connType = connType;
}
... ...
@Override public String toString() {
// Modify: 增加变量 connType
return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId + ", roaming="
+ roaming + ", typeId=" + typeId + ", visible=" + visible + ", connType=" + connType + ")";
}
}
继续 setMobileIcons 设置刷新 MobileIconState 流程:
StatusBarIconControllerImpl.setMobileIcons -> StatusBarIconControllerImpl.handleSet -> StatusBarIconController.onSetIconHolder -> StatusBarIconController.onSetMobileIcon
这一段不需要修改,作用就是将 MobileIconState 状态传到 StatusBarMobileView 中。
//StatusBarIconController.java
public void onSetMobileIcon(int viewIndex, MobileIconState state) {
StatusBarMobileView view = (StatusBarMobileView) mGroup.getChildAt(viewIndex);
if (view != null) {
view.applyMobileState(state);
}
if (mIsInDemoMode) {
mDemoStatusIcons.updateMobileState(state);
}
}
在 StatusBarMobileView 中根据 MobileIconState 状态数据进行刷新。需要修改两处:初始化及更新的函数。
//StatusBarMobileView.java
private void initViewState() {
... ...
mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
mOut.setVisibility(mState.activityOut ? View.VISIBLE : View.GONE);
//Modify: 根据 MobileIconState 中状态,控制图标
//mInoutContainer.setVisibility((mState.activityIn || mState.activityOut)
mInoutContainer.setVisibility(mState.connType == 1
? View.VISIBLE : View.GONE);
... ...
}
private boolean updateState(MobileIconState state) {
... ...
mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
mOut.setVisibility(state.activityOut ? View.VISIBLE : View.GONE);
//Modify: 根据 MobileIconState 中状态,控制图标
// mInoutContainer.setVisibility((state.activityIn || state.activityOut)
Log.i(TAG, "updateState " + state.connType);
mInoutContainer.setVisibility(state.connType == 1
? View.VISIBLE : View.GONE);
... ...
}