Android8.1 MTK平台 SystemUI源码分析之 网络信号栏显示刷新(上)

简介: 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 详解


20190527212955195.png



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

目录
相关文章
|
9天前
|
NoSQL 关系型数据库 MySQL
《docker高级篇(大厂进阶):4.Docker网络》包括:是什么、常用基本命令、能干嘛、网络模式、docker平台架构图解
《docker高级篇(大厂进阶):4.Docker网络》包括:是什么、常用基本命令、能干嘛、网络模式、docker平台架构图解
107 56
《docker高级篇(大厂进阶):4.Docker网络》包括:是什么、常用基本命令、能干嘛、网络模式、docker平台架构图解
|
2月前
|
Java Android开发 Swift
安卓与iOS开发对比:平台选择对项目成功的影响
【10月更文挑战第4天】在移动应用开发的世界中,选择合适的平台是至关重要的。本文将深入探讨安卓和iOS两大主流平台的开发环境、用户基础、市场份额和开发成本等方面的差异,并分析这些差异如何影响项目的最终成果。通过比较这两个平台的优势与挑战,开发者可以更好地决定哪个平台更适合他们的项目需求。
122 1
|
2月前
|
安全 物联网 物联网安全
量子通信网络:安全信息交换的新平台
【10月更文挑战第6天】量子通信网络作为一种全新的安全信息交换平台,正逐步展现出其独特的优势和巨大的潜力。通过深入研究和不断探索,我们有理由相信,量子通信网络将成为未来信息安全领域的重要支柱,为构建更加安全、高效、可靠的信息社会贡献力量。让我们共同期待量子通信网络在未来的广泛应用和美好前景!
|
3月前
|
IDE Android开发 iOS开发
探索Android与iOS开发的差异:平台选择对项目成功的影响
【9月更文挑战第27天】在移动应用开发的世界中,Android和iOS是两个主要的操作系统平台。每个系统都有其独特的开发环境、工具和用户群体。本文将深入探讨这两个平台的关键差异点,并分析这些差异如何影响应用的性能、用户体验和最终的市场表现。通过对比分析,我们将揭示选择正确的开发平台对于确保项目成功的重要作用。
|
20天前
|
IDE 开发工具 Android开发
移动应用开发之旅:探索Android和iOS平台
在这篇文章中,我们将深入探讨移动应用开发的两个主要平台——Android和iOS。我们将了解它们的操作系统、开发环境和工具,并通过代码示例展示如何在这两个平台上创建一个简单的“Hello World”应用。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧,帮助你更好地理解和掌握移动应用开发。
44 17
|
2月前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
116 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
3月前
|
开发工具 Android开发 iOS开发
安卓与iOS开发环境对比:选择适合你的平台
【9月更文挑战第26天】在移动应用开发的广阔天地中,安卓和iOS是两大巨头。它们各自拥有独特的优势和挑战,影响着开发者的选择和决策。本文将深入探讨这两个平台的开发环境,帮助你理解它们的核心差异,并指导你根据个人或项目需求做出明智的选择。无论你是初学者还是资深开发者,了解这些平台的异同都至关重要。让我们一起探索,找到最适合你的那片开发天地。
|
3月前
|
Android开发 开发者
Android平台无纸化同屏如何实现实时录像功能
Android平台无纸化同屏,如果需要本地录像的话,实现难度不大,只要复用之前开发的录像模块的就可以,对我们来说,同屏采集这块,只是数据源不同而已,如果是自采集的其他数据,我们一样可以编码录像。
|
20天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
40 19
|
20天前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
45 14