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文件

在這里插入圖片描述

drawable.zip


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM