SystemUI 的信号栏修改以及配置流程 8.0 9.0

包括 8.0  9.0 的流程和修改方式

一、 信号级别

二、 图片配置

三、android 8.0 代码中的修改 及 流程

四、android 9.0 的修改和 基本流程   

包括部分修改signal out in 图片自定义


8.0 
    NetworkControllerImpl 中收到广播更新数据时,调用到 MobileSignalController 生成各种数据, 
    通过 MobileSignalController notifyListeners 方法将数据回调给mSignalCallbacks
    通过 SignalClusterView 通过代码 mNetworkController.addCallback(this) 将自己添加到CallbackHandler中的 mSignalCallbacks 回调列表。

9.0 
    前面是一样的  MobileSignalController 调用到了"代理类" StatusBarSignalPolicy
    StatusBarSignalPolicy 通过 mIconController.setMobileIcons  调用到了具体实现类 StatusBarIconControllerImpl
    StatusBarIconControllerImpl
  
 

 

一、 信号级别

frameworks/base/telephony/java/android/telephony/SignalStrength.java

/** @hide */
private static final boolean mIsSignalFiveNumber = true;
/** @hide */
public static final int NUM_SIGNAL_STRENGTH_BINS = mIsSignalFiveNumber ? 6 : 5;

修改方式:查看好当前项目的level

二、 图片配置

2.1 图片配置加载类

SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java

//level
static final int SIGNAL_LEVEL_NUM = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
//图片数组
static String[] mSignalStrengthArray, mSignalStrengthRoamingArray;

//读取xml图片配置
static void readIconsFromXml(Context context) {
  if (isInitiated) {
  log(TAG, "readIconsFromXml, already read!");
        return;
  }

  mSignalStrengthArray = mRes.getStringArray(R.array.multi_signal_strength);
  mSignalStrengthRoamingArray = mRes.getStringArray(
        R.array.multi_signal_strength_roaming);
}

//更新 数据类型
static void updateDataType(int slot, int type, boolean showAtLeast3G,
        boolean show4GforLte, boolean hspaDistinguishable, int inet) {
      ....
      .....
      ....
}

//生成信号图标
static int getSignalStrengthIcon(int slot, int inet, int level, boolean roaming) {
  log(TAG, "getSignalStrengthIcon: " + String.format(
  "slot=%d, inetCondition=%d, level=%d, roaming=%b", slot, inet, level, roaming));

   String[] signalStrengthArray, selectedTypeArray;

   signalStrengthArray = mRes.getStringArray(mRes.getIdentifier(!roaming ?
   mSignalStrengthArray[slot] : mSignalStrengthRoamingArray[slot], null, NS));
   log(TAG, String.format("signalStrengthArray.length=%d", signalStrengthArray.length));

   selectedTypeArray = mRes.getStringArray(mRes.getIdentifier(
            signalStrengthArray[mSelectedSignalStreagthIndex[slot]], null, NS));
   log(TAG, String.format("selectedTypeArray.length=%d", selectedTypeArray.length));

   String[] inetArray = mRes.getStringArray(
   mRes.getIdentifier(selectedTypeArray[inet], null, NS));
   log(TAG, String.format("inetArray.length=%d", inetArray.length));

   return mRes.getIdentifier(inetArray[level], null, NS);
}


2.2 图片配置

通过二中读取的 array名称,就找到了读取的配置数据

SystemUI/res/values/arrays.xml   对应的sim1 sim2的图

<!--signal strength-->
<!--Add three items to support TSTS-->
<string-array name="multi_signal_strength">
    <item>array/telephony_siganl_strength_sim1</item>
    <item>array/telephony_siganl_strength_sim1</item>
    <item>array/telephony_siganl_strength_sim1</item>
</string-array>

<string-array name="telephony_siganl_strength_sim1">
 ...
</string-array>


修改方式:
根据实际的数组中配置的图片名称替换下面的图片
stat_sys_signal_0_fully.png      stat_sys_signal_0.png
stat_sys_signal_1_fully.png      stat_sys_signal_1.png
stat_sys_signal_2_fully.png      stat_sys_signal_2.png
stat_sys_signal_3_fully.png      stat_sys_signal_3.png
stat_sys_signal_4_fully.png      stat_sys_signal_4.png
stat_sys_signal_disable.png      stat_sys_signal_null.png

三、android 8.0 代码中的修改 及 流程

3.1 SystemUI/res/values/config.xml

<bool name="config_read_icons_from_xml">true</bool> -- 信号读取图片

加载view:              SignalClusterView.java  -> signal_cluster_view.xml 

信号图片控制的主要逻辑 :MobileSignalController.java
3.2  8.0 基本流程:

状态栏,锁屏状态栏,下拉通知状态栏等都有信号网络的控制,
 
(1)StatusBar{
        ...
	private NotificationMessagingUtil mMessagingUtil;
	private KeyguardUserSwitcher mKeyguardUserSwitcher;
	private UserSwitcherController mUserSwitcherController;
	private NetworkController mNetworkController;
	private KeyguardMonitorImpl mKeyguardMonitor
	= (KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class);
	private BatteryController mBatteryController;

	@Override
	public void start() {
	mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
	//生成网络控制器
	mNetworkController = Dependency.get(NetworkController.class);
	    ...
     }
}
  

(2) 加载 NetworkController 的实现类 NetworkControllerImpl

NetworkControllerImpl是 作为信号栏数据控制类,负责监控 wifi, service state 飞行模式等。

public class NetworkControllerImpl extends BroadcastReceiver ....{

@VisibleForTesting
final WifiSignalController mWifiSignalController;

@VisibleForTesting
final EthernetSignalController mEthernetSignalController;

@VisibleForTesting
final SparseArray<MobileSignalController> mMobileSignalControllers = new SparseArray<>();
// When no SIMs are around at setup, and one is added later, it seems to default to the first
// SIM for most actions.  This may be null if there aren't any SIMs around.
private MobileSignalController mDefaultSignalController;


//添加对应subid的 MobileSignalControll 控制器
private SubscriptionInfo addSignalController(int id, int simSlotIndex) {
    SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0,
            null, 0, 0, "");

MobileSignalController controller = new MobileSignalController(mContext,
mConfig, mHasMobileDataFeature, mPhone, mCallbackHandler, this, info,
mSubDefaults, mReceiverHandler.getLooper());
mMobileSignalControllers.put(id, controller);
controller.getState().userSetup = true;
    return info;
}



//这个构造方法内部会先去调用一个另外的构造方法;
/**
 * Construct this controller object and register for updates.
 */
public NetworkControllerImpl(Context context, Looper bgLooper,
DeviceProvisionedController deviceProvisionedController) {
this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE),
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE),
(WifiManager) context.getSystemService(Context.WIFI_SERVICE),
SubscriptionManager.from(context), Config.readConfig(context), bgLooper,
            new CallbackHandler(),
            new AccessPointControllerImpl(context, bgLooper),
            new DataUsageController(context),
            new SubscriptionDefaults(),
deviceProvisionedController);
// 主要注册监听的地方
mReceiverHandler.post(mRegisterListeners);
}


@VisibleForTesting
NetworkControllerImpl(Context context, ConnectivityManager connectivityManager,
TelephonyManager telephonyManager, WifiManager wifiManager,
SubscriptionManager subManager, Config config, Looper bgLooper,
CallbackHandler callbackHandler,
AccessPointControllerImpl accessPointController,
DataUsageController dataUsageController,
SubscriptionDefaults defaultsHandler,
DeviceProvisionedController deviceProvisionedController) {
mContext = context;
mConfig = config;
mReceiverHandler = new Handler(bgLooper);
mCallbackHandler = callbackHandler;
mDataSaverController = new DataSaverControllerImpl(context);

mSubscriptionManager = subManager;
mSubDefaults = defaultsHandler;
mConnectivityManager = connectivityManager;
mHasMobileDataFeature = mConnectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);

// telephony
mPhone = telephonyManager;

// wifi
mWifiManager = wifiManager;

mLocale = mContext.getResources().getConfiguration().locale;
mAccessPoints = accessPointController;
mDataUsageController = dataUsageController;
mDataUsageController.setNetworkController(this);
// TODO: Find a way to move this into DataUsageController.
mDataUsageController.setCallback(new DataUsageController.Callback() {
@Override
public void onMobileDataEnabled(boolean enabled) {
mCallbackHandler.setMobileDataEnabled(enabled);
}
    });
mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature,
mCallbackHandler, this);

mEthernetSignalController = new EthernetSignalController(mContext, mCallbackHandler, this);

// AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it
updateAirplaneMode(true /* force callback */);
mUserTracker = new CurrentUserTracker(mContext) {
@Override
public void onUserSwitched(int newUserId) {
            NetworkControllerImpl.this.onUserSwitched(newUserId);
}
    };
mUserTracker.startTracking();
deviceProvisionedController.addCallback(new DeviceProvisionedListener() {
@Override
public void onUserSetupChanged() {
            setUserSetupComplete(deviceProvisionedController.isUserSetup(
deviceProvisionedController.getCurrentUser()));
  }
    });
}



/**
 * Used to register listeners from the BG Looper, this way the PhoneStateListeners that
 * get created will also run on the BG Looper.
 */
private final Runnable mRegisterListeners = new Runnable() {
  @Override
  public void run() {
        registerListeners();
  }
};


private void registerListeners() {
   //手机信号控制器
   for (int i = 0; i < mMobileSignalControllers.size(); i++) {
        MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
   mobileSignalController.registerListener();
   }
   if (mSubscriptionListener == null) {
   mSubscriptionListener = new SubListener();
   }
   mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener);
	// broadcasts  这里就可以看到 有注册了 wifi,信号,数据流量 飞行模式,vpn
	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);
	mContext.registerReceiver(this, filter, null, mReceiverHandler);
	mListening = true;
	updateMobileControllers();
}


//从 config.xml 中读取的网络的配置
@VisibleForTesting
static class Config {
    boolean showAtLeast3G = false;
    boolean alwaysShowCdmaRssi = false;
    boolean show4gForLte = false;
    boolean hideLtePlus = false;
    boolean hspaDataDistinguishable;
    boolean readIconsFromXml;
    boolean showRsrpSignalLevelforLTE;
    boolean inflateSignalStrengths = false;
    static Config readConfig(Context context) {
        Config config = new Config();
        Resources res = context.getResources();
        config.showAtLeast3G = res.getBoolean(R.bool.config_showMin3G);
        config.alwaysShowCdmaRssi =
                res.getBoolean(com.android.internal.R.bool.config_alwaysUseCdmaRssi);
        config.show4gForLte = res.getBoolean(R.bool.config_show4GForLTE);
        config.hspaDataDistinguishable =
                res.getBoolean(R.bool.config_hspa_data_distinguishable);
        config.hideLtePlus = res.getBoolean(R.bool.config_hideLtePlus);
        config.readIconsFromXml = res.getBoolean(R.bool.config_read_icons_from_xml);
        config.showRsrpSignalLevelforLTE = res.getBoolean(R.bool.config_showRsrpSignalLevelforLTE);
        config.inflateSignalStrengths = res.getBoolean(R.bool.config_inflateSignalStrength);
        return config;
   }
  }
}



(3) 信号图片控制的主要逻辑类

public MobileSignalController(
   ....
  //如果配置读取图片,则加载配置类
 if (config.readIconsFromXml) {
      TelephonyIcons.readIconsFromXml(context);
     mDefaultIcons = !mConfig.showAtLeast3G ? TelephonyIcons.G : TelephonyIcons.THREE_G;
   } else {
     mapIconSets();
    }
 }


@Override
public int getCurrentIconId() {
   if (mCurrentState.iconGroup == TelephonyIcons.CARRIER_NETWORK_CHANGE) {
     return SignalDrawable.getCarrierChangeState(getNumLevels());
   } else if (mCurrentState.connected) {
      int level = mCurrentState.level;
        if (mConfig.inflateSignalStrengths) {
            level++;
   }
   if (mConfig.readIconsFromXml) {
      //返回配置类的图片
      return getIcons().mSingleSignalIcon;
   } else {
       return SignalDrawable.getState(level, getNumLevels(),
       mCurrentState.inetCondition == 0);
   }
  
  } else if (mCurrentState.enabled) {
   if (mConfig.readIconsFromXml) {
    return getIcons().mSbDiscState;
   } else {
   return SignalDrawable.getEmptyState(getNumLevels());
  }
    } else {
  return 0;
  }
}


 private int getSimSlotIndex() {
        int slotId = -1;
        if (mSubscriptionInfo != null) {
            slotId = mSubscriptionInfo.getSimSlotIndex();
        }
        if (DEBUG) Log.d(mTag, "getSimSlotIndex, slotId: " + slotId);
        return slotId;
}


//这个方法就生成 dataType, singleSignalIcon 的主要类了

private void generateIconGroup() {
    final int level = mCurrentState.level;
    final int voiceLevel = mCurrentState.voiceLevel;
    final int inet = mCurrentState.inetCondition;
    final boolean dataConnected = mCurrentState.dataConnected;
    final boolean roaming = isRoaming();
    final int voiceType = getVoiceNetworkType();
    int dataType =  getDataNetworkType();

    if (dataType == TelephonyManager.NETWORK_TYPE_LTE && mServiceState != null &&
    mServiceState.isUsingCarrierAggregation()) {
          dataType = TelephonyManager.NETWORK_TYPE_LTE_CA;
}


int[] contentDesc = AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH;
    int discContentDesc = AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0];
    int dataContentDesc, dataTypeIcon, qsDataTypeIcon, dataActivityId;
    int singleSignalIcon, stackedDataIcon = 0, stackedVoiceIcon = 0;

    final int slotId = getSimSlotIndex();
    if (slotId < 0 || slotId > mPhone.getPhoneCount()) {
        Log.e(mTag, "generateIconGroup invalid slotId:" + slotId);
        return;
}

if (DEBUG) Log.d(mTag, "generateIconGroup slot:" + slotId + " style:" + mStyle
+ " connected:" + mCurrentState.connected + " inetCondition:" + inet
            + " roaming:" + roaming + " level:" + level + " voiceLevel:" + voiceLevel
            + " dataConnected:" + dataConnected
            + " dataActivity:" + mCurrentState.dataActivity
+ " CS:" + voiceType
            + "/" + TelephonyManager.getNetworkTypeName(voiceType)
            + ", PS:" + dataType
            + "/" + TelephonyManager.getNetworkTypeName(dataType));

// Update data icon set
int chosenNetworkType = ((dataType == TelephonyManager.NETWORK_TYPE_UNKNOWN)
            ? voiceType : dataType);
TelephonyIcons.updateDataType(slotId, chosenNetworkType, mConfig.showAtLeast3G,
mConfig.show4gForLte, mConfig.hspaDataDistinguishable, inet);

// Update signal strength icons ---
singleSignalIcon = TelephonyIcons.getSignalStrengthIcon(slotId, inet, level, roaming);
    if (DEBUG) {
        Log.d(mTag, "singleSignalIcon:" + getResourceName(singleSignalIcon));
}

    dataActivityId = (mCurrentState.dataConnected && slotId >= 0) ?
            TelephonyIcons.getDataActivity(slotId, mCurrentState.dataActivity) : 0;

// Convert the icon to unstacked if necessary.
int unstackedSignalIcon = TelephonyIcons.convertMobileStrengthIcon(singleSignalIcon);
    if (DEBUG) {
        Log.d(mTag, "unstackedSignalIcon:" + getResourceName(unstackedSignalIcon));
}
if (singleSignalIcon != unstackedSignalIcon) {
        stackedDataIcon = singleSignalIcon;
singleSignalIcon = unstackedSignalIcon;
}

int[] subId = SubscriptionManager.getSubId(getSimSlotIndex());
    if (subId != null && subId.length >= 1) {
mDualBar = SubscriptionManager.getResourcesForSubId(mContext,
subId[0]).getBoolean(com.android.internal.R.bool.config_dual_bar);
}

if (DEBUG) {
        Log.d(mTag, "mDualBar:" + mDualBar);
Log.d(mTag, "mStyle:" + mStyle);
}

if (mStyle == STATUS_BAR_STYLE_CDMA_1X_COMBINED
|| (mStyle == STATUS_BAR_STYLE_DATA_VOICE && mDualBar)) {
if (!roaming && showDataAndVoice()) {
            stackedVoiceIcon = TelephonyIcons.getStackedVoiceIcon(voiceLevel);
} else if (roaming && dataActivityId != 0) {
// Remove data type indicator if already shown in data activity icon.
singleSignalIcon = TelephonyIcons.getRoamingSignalIconId(level, inet);
}
    }

// Clear satcked data icon if no satcked voice icon.
if (stackedVoiceIcon == 0) stackedDataIcon = 0;

contentDesc = TelephonyIcons.getSignalStrengthDes(slotId);
    int sbDiscState = TelephonyIcons.getSignalNullIcon(slotId);
    if (DEBUG) {
        Log.d(mTag, "singleSignalIcon=" + getResourceName(singleSignalIcon)
                + " dataActivityId=" + getResourceName(dataActivityId)
                + " stackedDataIcon=" + getResourceName(stackedDataIcon)
                + " stackedVoiceIcon=" + getResourceName(stackedVoiceIcon));
}

// Update data net type icons
if (dataType == TelephonyManager.NETWORK_TYPE_IWLAN) {
// wimax is a special 4g network not handled by telephony
dataTypeIcon = TelephonyIcons.ICON_4G;
qsDataTypeIcon = TelephonyIcons.ICON_4G;
dataContentDesc = R.string.accessibility_data_connection_4g;
} else {
        dataTypeIcon = TelephonyIcons.getDataTypeIcon(slotId);
dataContentDesc = TelephonyIcons.getDataTypeDesc(slotId);
qsDataTypeIcon = TelephonyIcons.getQSDataTypeIcon(slotId);
}

if (DEBUG) {
        Log.d(mTag, "updateDataNetType, dataTypeIcon=" + getResourceName(dataTypeIcon)
                + " qsDataTypeIcon=" + getResourceName(qsDataTypeIcon)
                + " dataContentDesc=" + dataContentDesc);
}

mCurrentState.iconGroup = new MobileIconGroup(
            TelephonyManager.getNetworkTypeName(dataType),
            null, null, contentDesc, 0, 0, sbDiscState, 0, discContentDesc,dataContentDesc, dataTypeIcon, false, qsDataTypeIcon,
     singleSignalIcon, stackedDataIcon, stackedVoiceIcon, dataActivityId);
   }



@Override
public void notifyListeners(SignalCallback callback) {

   //8.0之前会在这里判断,如果是读取配置,则去生成对应图片
   if (mConfig.readIconsFromXml) {
        generateIconGroup();
   }
   .....
   .....
    
   //这里很重要的, 就是将数据回调给 CallbackHandler,  CallbackHandler, 再
   callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
    activityIn, activityOut, dataActivityId,
    icons.mStackedDataIcon, icons.mStackedVoiceIcon,
    dataContentDescription, description, icons.mIsWide,
    mSubscriptionInfo.getSubscriptionId(), mCurrentState.roaming);
  }


}  



(4) MobileSignalController 回调到 CallbackHandler 回调到  SignalClusterView 

class CallbackHandler{
	 @Override
	public void setMobileDataIndicators(final IconState statusIcon, final IconState qsIcon,
		final int statusType, final int qsType,final boolean activityIn,
		final boolean activityOut, final int dataActivityId,
		final int stackedDataIcon, final int stackedVoiceIcon,
		final String typeContentDescription,
		final String description, final boolean isWide, final int subId, boolean roaming) {
	    post(new Runnable() {
	@Override
	public void run() {
	for (SignalCallback signalCluster : mSignalCallbacks) {

        //回调到了SignalClusterView 
	signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType,
	activityIn, activityOut, dataActivityId,
	stackedDataIcon, stackedVoiceIcon,
	typeContentDescription, description, isWide,
	subId, roaming);
	}
		}
	    });
	}
}


SignalClusterView.java

public class SignalClusterView extends LinearLayout implements NetworkControllerImpl.SignalCallback,
        SecurityController.SecurityControllerCallback, Tunable,DarkReceiver {



@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mVpnVisible = mSecurityController.isVpnEnabled();
mVpnIconId = currentVpnIconId(mSecurityController.isVpnBranded());

    for (PhoneState state : mPhoneStates) {
       if (state.mMobileGroup.getParent() == null) {
      mMobileSignalGroup.addView(state.mMobileGroup);
     }
    }

    int endPadding = mMobileSignalGroup.getChildCount() > 0 ? mMobileSignalGroupEndPadding : 0;
    mMobileSignalGroup.setPaddingRelative(0, 0, endPadding, 0);

    Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);

    apply();
    applyIconTint();
    //在这里,将自己添加到了回调列表中
    mNetworkController.addCallback(this);
    mSecurityController.addCallback(this);
   }

   public void setMobileDataIndicators(...){
        ....
       apply();
   }

}

四、android 9.0 的修改和 基本流程

4.1 

SystemUI/res/values/config.xml

<bool name="config_read_icons_from_xml">true</bool> -- 信号读取图片
<bool name="config_alwaysShowTypeIcon">true</bool>  -- 显示类型 4G 3G


信号栏图片的逻辑:MobileSignalController.java (9.0 代码变化较大) 

//一、二修改的步骤一致。
//主要需要修改  MobileSignalController.java
//MobileSignalController.java 中将

MobileSignalController.java {

    public MobileSignalController(
   
      //9.0 没有了 TelephonyIcons的加载
      //if (config.readIconsFromXml) {
      // TelephonyIcons.readIconsFromXml(context);
      // mDefaultIcons = !mConfig.showAtLeast3G ? TelephonyIcons.G : TelephonyIcons.THREE_G;
      //} else {
    
      mapIconSets();
      ...
    }

     //需要修改,这个方法返回的是 信号栏需要显示的图片ID,9.0中完全使用  SignalDrawable 来生成
     @Override
     public int getCurrentIconId() {
       //9.0 可以自己添加
       if (mConfig.readIconsFromXml) {
           return getIcons().mSingleSignalIcon;
       }
     }


     //信号状态发生变化的时候,会调用到这里,在这里,这里会生成当前很多的信息
     @Override
     public void notifyListeners(SignalCallback callback) {

       //9.0 去掉了  generateIconGroup code 需要补充
       //if (mConfig.readIconsFromXml) {
       // generateIconGroup();
       //}
       // 在  generateIconGroup 中也不能生成 使用new 的代码了
       // 需要将最后1个生成对象代码变成  mSingleSignalIcon 这个属性需要自己添加9.0 已经去掉了
       // mCurrentState.iconGroup.mSingleSignalIcon = singleSignalIcon;

       ....
       ....

       //从这里把 新的状态回调到,  StatusBarSignalPolicy.java
       callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
                activityIn, activityOut,volteIcon,
                dataContentDescription, description, icons.mIsWide,
                mSubscriptionInfo.getSubscriptionId(), mCurrentState.roaming);

      }

}


信号栏界面view的修改 : StatusBarMobileView.java
StatusBarMobileView.java{

 在初始化中添加流量图标
 private void initViewState() {
     //隐藏原生container
     mInoutContainer.setVisibility(View.GONE);
      //添加view
     mInOut.setImageResource(getCardActivityId(mState.activityIn,mState.activityOut));

  }     

   //更新状态
   private void updateState(MobileIconState state) {
        setContentDescription(state.contentDescription);
        if (mState.visible != state.visible) {
            mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE);
        }
     
        if (mState.strengthId != state.strengthId) {
              //更改信号View图片显示方式 
              //mMobileDrawable.setLevel(state.strengthId);    
              mMobile.setImageResource(state.strengthId);
            }
        }

        if (mState.typeId != state.typeId) {
            if (state.typeId != 0) {
                mMobileType.setContentDescription(state.typeContentDescription);
                mMobileType.setImageResource(state.typeId);

                //更新上下行图标
                mInOut.setImageResource(getCardActivityId(mState.activityIn,mState.activityOut));
                mMobileType.setVisibility(View.VISIBLE);
            } else {
                mMobileType.setVisibility(View.GONE);
            }
        }

        mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
        mMobileRoamingSpace.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
        mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
        mOut.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);

       //  mInoutContainer.setVisibility((state.activityIn || state.activityOut)
       //             ? View.VISIBLE : View.GONE);
       //  隐藏原生的效果
       //  mInoutContainer.setVisibility(View.GONE);
      
    } 


  @Override
    public void onDarkChanged(Rect area, float darkIntensity, int tint) {
        if (!isInArea(area, this)) {
            return;
        }
        mMobileDrawable.setDarkIntensity(darkIntensity);
        ColorStateList color = ColorStateList.valueOf(getTint(area, this, tint));
        mIn.setImageTintList(color);
        mOut.setImageTintList(color);
        mMobileType.setImageTintList(color);
        mMobileRoaming.setImageTintList(color);
        mDotView.setDecorColor(tint);
        mDotView.setIconColor(tint, false);
        //添加图标更改颜色
        mInOut.setImageTintList(color);
        mMobile.setImageTintList(color);
    }

   //添加流量图标的定义
   private int getCardActivityId(boolean activityIn, boolean activityOut) {
        int activityId;
        if (activityIn && activityOut) {
            activityId = R.drawable.stat_sys_signal_inout_auto_mirrored;
        } else if (activityIn) {
            activityId = R.drawable.stat_sys_signal_in_auto_mirrored;
        } else if (activityOut) {
            activityId = R.drawable.stat_sys_signal_out_auto_mirrored;
        } else {
            activityId = R.drawable.stat_sys_signal_no_auto_mirrored;
        }
        return activityId;
    }

}

4.2 基本流程


前面的流程基本一致

MobileSignalController 调用到  StatusBarSignalPolicy.java


代理类 
StatusBarSignalPolicy.java{

         public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,{
         在此类中,就真正的进行的数据赋值
	 MobileIconState state = getState(subId);
	 state.visible = statusIcon.visible && !mBlockMobile;
	 state.strengthId = statusIcon.icon;    -- 信号栏图片ID
	 state.typeId = statusType;                  --  信号类型图片
	 state.contentDescription = statusIcon.contentDescription;  --描述
	 state.typeContentDescription = typeContentDescription;
	 state.roaming = roaming;                  -- 漫游
	 state.activityIn = activityIn && mActivityEnabled;   -- 数据进入
	 state.activityOut = activityOut && mActivityEnabled;
	 state.volteId = volteIcon;

         // Always send a copy to maintain value type semantics  
         这里调用到拉具体实现类 StatusBarIconControllerImpl.java 
         mIconController.setMobileIcons(mSlotMobile, MobileIconState.copyStates(mMobileStates));
        ....
       }
}


//具体控制实现类  XXXimpl.java
StatusBarIconControllerImpl.java  {

     @Override
     public void setMobileIcons(String slot, List<MobileIconState> iconStates) {
       Slot mobileSlot = getSlot(slot);
       int slotIndex = getSlotIndex(slot);
       for (MobileIconState state : iconStates) {
           StatusBarIconHolder holder = mobileSlot.getHolderForTag(state.subId);
           if (holder == null) {
              holder = StatusBarIconHolder.fromMobileIconState(state);
              setIcon(slotIndex, holder);
            } else {
              holder.setMobileState(state);
              handleSet(slotIndex, holder);
          }
        }
      }

      @Override
      public void setIcon(int index, @NonNull StatusBarIconHolder holder) {
         boolean isNew = getIcon(index, holder.getTag()) == null;
	 super.setIcon(index, holder);
	 if (isNew) {
		addSystemIcon(index, holder);
	 } else {
		handleSet(index, holder);
	 }
      }

     //如下  addSystemIcon 和  handleSet 会调用到 IconManager 中的方法进行实现,
     //具体看下下面IconManager的部分代码

     private void addSystemIcon(int index, StatusBarIconHolder holder) {
       String slot = getSlotName(index);
       int viewIndex = getViewIndex(index, holder.getTag());
       boolean blocked = mIconBlacklist.contains(slot);
       mIconLogger.onIconVisibility(getSlotName(index), holder.isVisible());
       //for 循环添加      mIconGroups   中的数据
       // onIconAdded 这个方法,就可以看到  IconManager 中的实现,会判断type进行add
       mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, holder));
     }

     private void handleSet(int index, StatusBarIconHolder holder) {
       int viewIndex = getViewIndex(index, holder.getTag());
       mIconLogger.onIconVisibility(getSlotName(index), holder.isVisible());
       mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder));
      }


  
   //这个类也是很重要的, 这里保存了    mIconGroups
   //QuickStatusBarHeader,  CollapsedStatusBarFragment
   //Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
 

   @Override
   public void addIconGroup(IconManager group) {
       mIconGroups.add(group);
       List<Slot> allSlots = getSlots();
       for (int i = 0; i < allSlots.size(); i++) {
       Slot slot = allSlots.get(i);
       List<StatusBarIconHolder> holders = slot.getHolderListInViewOrder();
       boolean blocked = mIconBlacklist.contains(slot.getName());

        for (StatusBarIconHolder holder : holders) {
        int tag = holder.getTag();
        int viewIndex = getViewIndex(getSlotIndex(slot.getName()), holder.getTag());
        group.onIconAdded(viewIndex, slot.getName(), blocked, holder);
   }
    }
}




public interface StatusBarIconController {

//statuabr icon 黑名单,
public static final String ICON_BLACKLIST = "icon_blacklist";
     

//继承 iconManager, 然后去控制状态栏的颜色    

public static class DarkIconManager extends IconManager { }
public static class TintedIconManager extends IconManager { }
  

 内部类 
 Class IconManager{
  
   //Turns info from StatusBarIconController into ImageViews in a ViewGroup.
   public static class IconManager implements DemoMode {

    protected void onIconAdded(int index, String slot, boolean blocked,

    StatusBarIconHolder holder) {
       addHolder(index, slot, blocked, holder);
    }

   //生成缓存类 viewHoler    类似listiew 的 viewHolder
   protected StatusIconDisplayable addHolder(int index, String slot, boolean blocked,
	StatusBarIconHolder holder) {
	switch (holder.getType()) {
	case TYPE_ICON:
	  return addIcon(index, slot, blocked, holder.getIcon());

	case TYPE_WIFI:
	  return addSignalIcon(index, slot, holder.getWifiState());

	case TYPE_MOBILE:
	  return addMobileIcon(index, slot, holder.getMobileState());
	}
	return null;
	}

 
        protected StatusBarIconView addIcon(int index, String slot, boolean blocked,StatusBarIcon icon) {
        }
 
        //生成wifi相关view
        protected StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) {
        }

        //生成信号栏等手机相关的view
        protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) {
         }
        }
    }

}

  

demo

修改布局
/frameworks/base / packages/SystemUI/extApp/res/layout/status_bar_mobile_signal_group.xml

   <!--<LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
            <ImageView
                android:id="@+id/mobile_type"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:layout_gravity="center_vertical"
                android:paddingEnd="1dp"
                />
        </LinearLayout>-->

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="15dip"
            android:layout_gravity="center_vertical">
            <ImageView
                android:id="@+id/mobile_type"
                android:layout_width="wrap_content"
                android:layout_height="9dip"
                android:layout_alignParentTop="true"
                android:layout_centerHorizontal="true" />
            <ImageView
                android:id="@+id/mobile_inout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop = "3dip"
                android:layout_alignParentBottom="true"
                android:layout_centerHorizontal="true" />
        </RelativeLayout>


/frameworks/base / packages/SystemUI/extApp/res/values/config.xml

  <bool name="config_alwaysShowTypeIcon">true</bool>
    <bool name="config_read_icons_from_xml">true</bool>

frameworks/base / packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java

//隐藏wifi的流量箭头

 private void updateState(WifiIconState state) {
        setContentDescription(state.contentDescription);
        if (mState.resId != state.resId && state.resId >= 0) {
            NeutralGoodDrawable drawable = NeutralGoodDrawable
                    .create(mLightContext, mDarkContext, state.resId);
            drawable.setDarkIntensity(mDarkIntensity);
            mWifiIcon.setImageDrawable(drawable);
        }

        mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
        mOut.setVisibility(state.activityOut ? View.VISIBLE : View.GONE);

        //隐藏
        if (SystemUIFeature.SUPPORT_ROM) {
            mInoutContainer.setVisibility(View.GONE);
        } else {
            mInoutContainer.setVisibility( (state.activityIn || state.activityOut) ? View.VISIBLE : View.GONE);
        }

}

/frameworks/base / packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java

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;
        //------------------添加 SingleSignalIcon-- 标示要显示的图
        public int mSingleSignalIcon = 0;
        ......

}

/frameworks/base / packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java

public class MobileSignalController extends SignalController<
        MobileSignalController.MobileState, MobileSignalController.MobileIconGroup> {

   public MobileSignalController(Context context, Config config, boolean hasMobileData,
            TelephonyManager phone, CallbackHandler callbackHandler,
            NetworkControllerImpl networkController, SubscriptionInfo info,
            SubscriptionDefaults defaults, Looper receiverLooper) {

      ......

      //读取icon

      //-------add-------------------------------
        if(SystemUIFeature.SUPPORT_ROM){
            TelephonyIcons.readIconsFromXml(context);
        }
    ---------------------------------------------------

    }



@Override
    public int getCurrentIconId() {
       
        if (SystemUIFeature.SUPPORT_ROM) {
            if (mCurrentState.connected) {
                return mCurrentState.iconGroup.mSingleSignalIcon;
            }
            return super.getCurrentIconId();
        } else {
            if (mCurrentState.iconGroup == TelephonyIcons.CARRIER_NETWORK_CHANGE) {
                return SignalDrawable.getCarrierChangeState(getNumLevels());
            } else if (mCurrentState.connected) {
                int level = mCurrentState.level;
                if (mConfig.inflateSignalStrengths) {
                    level++;
                }
                boolean dataDisabled = mCurrentState.userSetup
                        && mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED;
                boolean noInternet = mCurrentState.inetCondition == 0;
                boolean cutOut = dataDisabled || noInternet;
                if (mHideNoInternetState) {
                    cutOut = false;
                }
                return SignalDrawable.getState(level, getNumLevels(), cutOut);
            } else if (mCurrentState.enabled) {
                return SignalDrawable.getEmptyState(getNumLevels());
            } else {
                return 0;
            }
        }
        //-bug 430593 lilei.wt,add for signal drawable,2018/3/9
    }



    @Override
    public void notifyListeners(SignalCallback callback) {
        

        //解析生成 icon group

        generateIconGroup();
       
        MobileIconGroup icons = getIcons();

       ..........

    }


   

   private int getSimSlotIndex() {
        int slotId = -1;
        if (mSubscriptionInfo != null) {
            slotId = mSubscriptionInfo.getSimSlotIndex();
        }
        if (DEBUG) Log.d(mTag, "getSimSlotIndex, slotId: " + slotId);
        return slotId;
    }

    private void generateIconGroup() {

        final int level = mCurrentState.level;
        final int inet = mCurrentState.inetCondition;
        final boolean dataConnected = mCurrentState.dataConnected;
        final boolean roaming = isRoaming();
        final int voiceType = getVoiceNetworkType();
        int dataType =  getDataNetworkType();
        if (dataType == TelephonyManager.NETWORK_TYPE_LTE && mServiceState != null &&
                mServiceState.isUsingCarrierAggregation()) {
            dataType = TelephonyManager.NETWORK_TYPE_LTE_CA;
        }
        int[] contentDesc = AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH;
        int discContentDesc = AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0];
        int dataContentDesc, dataTypeIcon, qsDataTypeIcon, dataActivityId;
        int singleSignalIcon, stackedDataIcon = 0, stackedVoiceIcon = 0;

        final int slotId = getSimSlotIndex();
        if (slotId < 0 || slotId > mPhone.getPhoneCount()) {
            Log.e(mTag, "generateIconGroup invalid slotId:" + slotId);
            return;
        }

        // Update data icon set
        int chosenNetworkType = ((dataType == TelephonyManager.NETWORK_TYPE_UNKNOWN) ? voiceType : dataType);
        TelephonyIcons.updateDataType(slotId, chosenNetworkType, mConfig.showAtLeast3G, mConfig.show4gForLte, mConfig.hspaDataDistinguishable, inet);

        // Update signal strength icons
        singleSignalIcon = TelephonyIcons.getSignalStrengthIcon(slotId, inet, level, roaming);
        int unstackedSignalIcon = TelephonyIcons.convertMobileStrengthIcon(singleSignalIcon);
        if (singleSignalIcon != unstackedSignalIcon) {
            stackedDataIcon = singleSignalIcon;
            singleSignalIcon = unstackedSignalIcon;
        }

        int[] subId = SubscriptionManager.getSubId(getSimSlotIndex());
        if (stackedVoiceIcon == 0) stackedDataIcon = 0;
        contentDesc = TelephonyIcons.getSignalStrengthDes(slotId);
        int sbDiscState = TelephonyIcons.getSignalNullIcon(slotId);

        if (dataType == TelephonyManager.NETWORK_TYPE_IWLAN) {
            // wimax is a special 4g network not handled by telephony
            dataTypeIcon = TelephonyIcons.ICON_4G;
            qsDataTypeIcon = TelephonyIcons.ICON_4G;
            //dataContentDesc = R.string.accessibility_data_connection_4g;
            dataContentDesc = TelephonyIcons.getDataTypeDesc(slotId);
        } else {
            dataTypeIcon = TelephonyIcons.getDataTypeIcon(slotId);
            dataContentDesc = TelephonyIcons.getDataTypeDesc(slotId);
            qsDataTypeIcon = TelephonyIcons.getQSDataTypeIcon(slotId);
        }

        if (DEBUG) Log.d(mTag, "xml signal:" + singleSignalIcon);
        mCurrentState.iconGroup.mSingleSignalIcon = singleSignalIcon;
        //mCurrentState.iconGroup = new MobileIconGroup(
        //        TelephonyManager.getNetworkTypeName(dataType),
        //        null, null, contentDesc, 0, 0, sbDiscState, 0, discContentDesc,
        //        dataContentDesc, dataTypeIcon, false, singleSignalIcon);
    }
    

}
public class StatusBarMobileView extends FrameLayout implements DarkReceiver,
        StatusIconDisplayable {



    private ImageView mIn;
    private ImageView mOut;
   

    //---添加 inOut
    private ImageView mInOut;



   private void init() {
        mMobileGroup = findViewById(R.id.mobile_group);
        mMobile = findViewById(R.id.mobile_signal);
        mMobileType = findViewById(R.id.mobile_type);
        mMobileRoaming = findViewById(R.id.mobile_roaming);
        mMobileRoamingSpace = findViewById(R.id.mobile_roaming_space);
        mIn = findViewById(R.id.mobile_in);
        mOut = findViewById(R.id.mobile_out);
        mInOut = findViewById(R.id.mobile_inout);

        //add in out view

        mInoutContainer = findViewById(R.id.inout_container);
        mVolte = findViewById(R.id.mobile_volte);

        mMobileDrawable = new SignalDrawable(getContext());

        if(!SystemUIFeature.SUPPORT_ROM){
            mMobile.setImageDrawable(mMobileDrawable);
        }
        ....
    }



 private void initViewState() {
        setContentDescription(mState.contentDescription);
        if (!mState.visible) {
            mMobileGroup.setVisibility(View.GONE);
        } else {
            mMobileGroup.setVisibility(View.VISIBLE);
        }

        ....

       //设置图片 ID

       if(SystemUIFeature.ROM){
            mMobile.setImageResource(mState.strengthId);
        }else{
            mMobileDrawable.setLevel(mState.strengthId);
        }

        if (mState.typeId > 0) {
            mMobileType.setContentDescription(mState.typeContentDescription);
            mMobileType.setImageResource(mState.typeId);
            mMobileType.setVisibility(View.VISIBLE);
        } else {
            mMobileType.setVisibility(View.GONE);
        }
        mMobileRoaming.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
        mMobileRoamingSpace.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
        mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE);
        mOut.setVisibility(mState.activityOut ? View.VISIBLE : View.GONE);
        mInOut.setImageResource(getCardActivityId(mState.activityIn,mState.activityOut));

        //设置原生 inout 隐藏
        if (SystemUIFeature.SUPPORT_ROM) {
            mInoutContainer.setVisibility(View.GONE);
        } else {
            mInoutContainer.setVisibility((mState.activityIn || mState.activityOut)
                    ? View.VISIBLE : View.GONE);
        }

        if (mState.volteId > 0 ) {
            mVolte.setImageResource(mState.volteId);
            mVolte.setVisibility(View.VISIBLE);
        }else {
            mVolte.setVisibility(View.GONE);
        }
    }



  private void updateState(MobileIconState state) {
        setContentDescription(state.contentDescription);
        if (mState.visible != state.visible) {
            mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE);
        }

       //更新设置 图片
        if (mState.strengthId != state.strengthId) {
            if (SystemUIFeature.SUPPORT_ROM) {
                mMobile.setImageResource(state.strengthId);
            } else {
                mMobileDrawable.setLevel(state.strengthId);
            }
        }
     
        if (mState.typeId != state.typeId) {
            if (state.typeId != 0) {
                mMobileType.setContentDescription(state.typeContentDescription);
                mMobileType.setImageResource(state.typeId);
                //设置图标

                mInOut.setImageResource(getCardActivityId(mState.activityIn,mState.activityOut));

                mMobileType.setVisibility(View.VISIBLE);
            } else {
                mMobileType.setVisibility(View.GONE);
            }
        }

        mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
        mMobileRoamingSpace.setVisibility(state.roaming ? View.VISIBLE : View.GONE);

        mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
        mOut.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);

        //隐藏原生 inoutContainer
        if (SystemUIFeature.SUPPORT_ROM) {
            mInoutContainer.setVisibility(View.GONE);
        } else {
            mInoutContainer.setVisibility((state.activityIn || state.activityOut)   ? View.VISIBLE : View.GONE);
        }

        if (mState.volteId != state.volteId) {
            if (state.volteId != 0) {
                mVolte.setImageResource(state.volteId);
                mVolte.setVisibility(View.VISIBLE);
            } else {
                mVolte.setVisibility(View.GONE);
            }
        }

        mState = state;
    }



    @Override
    public void onDarkChanged(Rect area, float darkIntensity, int tint) {
        if (!isInArea(area, this)) {
            return;
        }
        mMobileDrawable.setDarkIntensity(darkIntensity);
        ColorStateList color = ColorStateList.valueOf(getTint(area, this, tint));
        mIn.setImageTintList(color);
        mOut.setImageTintList(color);
        mMobileType.setImageTintList(color);
        mMobileRoaming.setImageTintList(color);
        mDotView.setDecorColor(tint);
        mDotView.setIconColor(tint, false);
        //onDarkChanged  设置 color
        mInOut.setImageTintList(color);
        mMobile.setImageTintList(color);

    }

    @Override
    public void setStaticDrawableColor(int color) {
        ColorStateList list = ColorStateList.valueOf(color);
        float intensity = color == Color.WHITE ? 0 : 1;
        mMobileDrawable.setDarkIntensity(intensity);
        mIn.setImageTintList(list);
        mOut.setImageTintList(list);
        mMobileType.setImageTintList(list);
        mMobileRoaming.setImageTintList(list);
        mDotView.setDecorColor(color);

        //设置color
        mInOut.setImageTintList(list);
        mMobile.setImageTintList(list);

    }




  //根据 mobile data in/out   返回

  private int getCardActivityId(boolean activityIn, boolean activityOut) {
        int activityIconId;
        if (activityIn && activityOut) {
            activityIconId = R.drawable.stat_sys_signal_inout_auto_mirrored;
        } else if (activityIn) {
            activityIconId = R.drawable.stat_sys_signal_in_auto_mirrored;
        } else if (activityOut) {
            activityIconId = R.drawable.stat_sys_signal_out_auto_mirrored;
        } else {
            activityIconId = R.drawable.stat_sys_signal_no_auto_mirrored;
        }
        return activityIconId;
    }
  

  图片-------

  /frameworks/base / packages/SystemUI/extApp/res/drawable/stat_sys_signal_in_auto_mirrored.xml

<bitmap xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res/com.android.email" android:src="@drawable/stat_sys_signal_in"/>


frameworks/base / packages/SystemUI/extApp/res/drawable/stat_sys_signal_no_auto_mirrored.xml

<bitmap xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res/com.android.email" android:src="@drawable/stat_sys_signal_no"/>

 

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

空白的泡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值