tether need something

111 阅读15分钟

Repository方法接口定义表

  1. PortableRouterRepository (pRSummaryRepository)

方法名方法签名说明用途getSummaryInfofun getSummaryInfo(): Observable获取便携路由器汇总信息获取路由器模式信息、多WAN状态等核心配置doConnectConnectionfun doConnectConnection(connectionType: String): Observable<>连接指定类型的网络连接启用特定网络连接类型(以太网/USB/热点)disconnectConnectionfun disconnectConnection(connectionType: String): Observable<>断开指定类型的网络连接禁用特定网络连接类型setNetworkInfofun setNetworkInfo(params: SetConnectionParam): Observable<*>设置网络信息配置网络连接模式和参数getPortableRouterSummaryInfofun getPortableRouterSummaryInfo(): PortableRouterBean?获取便携路由器汇总信息(同步)同步获取当前路由器状态信息portableRouterSummaryInfoLiveDataval portableRouterSummaryInfoLiveData: MutableLiveData<Resource>便携路由器汇总信息的LiveData提供响应式的路由器状态数据绑定

  1. PrInternetConnectionRepository (pRInternetConnectionRepository)

方法名方法签名说明用途getTurnAllPortsToLANInfofun getTurnAllPortsToLANInfo(): Observable<>获取所有端口转LAN信息支持网络连接V2版本的端口配置getUsbWanInfofun getUsbWanInfo(): Observable<>获取USB WAN连接信息获取USB网络连接的配置和状态getHotspotWanInfofun getHotspotWanInfo(): Observable<*>获取热点WAN连接信息获取热点模式下的WAN连接配置getConnCheckInfofun getConnCheckInfo(): Observable获取连接检测信息获取网络连接监测的配置参数setMacClonefun setMacClone(macCloneType: Byte, customMacAddress: String): Completable设置MAC克隆配置MAC地址克隆功能setConnCheckInfofun setConnCheckInfo(info: ConnectionCheckInfo): Completable设置连接检测信息配置网络连接监测参数setMultiWanInfofun setMultiWanInfo(params: MultiWanInfo): Completable设置多WAN信息配置多WAN负载均衡和备份设置reverseEthernetFoldStatusfun reverseEthernetFoldStatus(): Unit切换以太网折叠状态控制UI中以太网信息的展开/折叠显示reverseUsbFoldStatusfun reverseUsbFoldStatus(): Unit切换USB折叠状态控制UI中USB信息的展开/折叠显示reverseHotspotFoldStatusfun reverseHotspotFoldStatus(): Unit切换热点折叠状态控制UI中热点信息的展开/折叠显示getEthernetFoldStatusfun getEthernetFoldStatus(): Boolean获取以太网折叠状态获取以太网信息当前的显示状态getUsbFoldStatusfun getUsbFoldStatus(): Boolean获取USB折叠状态获取USB信息当前的显示状态getHotspotFoldStatusfun getHotspotFoldStatus(): Boolean获取热点折叠状态获取热点信息当前的显示状态connCheckInfoLiveDataval connCheckInfoLiveData: MutableLiveData<Resource>连接检测信息LiveData提供连接检测配置的响应式数据usbWanInfoLiveDataval usbWanInfoLiveData: LiveData<Resource>USB WAN信息LiveData提供USB连接信息的响应式数据turnAllPortsToLANInfoLiveDataval turnAllPortsToLANInfoLiveData: MutableLiveData<Resource>端口转LAN信息LiveData提供端口配置的响应式数据

  1. RouterWanRepository (wanRepository)

方法名方法签名说明用途getMACCloneInfofun getMACCloneInfo(): Observable<*>获取MAC克隆信息获取当前MAC克隆配置状态getWanConnInfoV40fun getWanConnInfoV40(): Observable获取WAN连接信息V4.0获取WAN连接的详细配置信息getPortableHotspotWanInfofun getPortableHotspotWanInfo(): Observable获取便携热点WAN信息获取热点模式下的WAN连接信息isConnectedViaATAfun isConnectedViaATA(): Boolean检查是否通过ATA连接判断当前是否使用ATA连接方式setWanConnInfoV40fun setWanConnInfoV40(params: WanInfoParams): Completable设置WAN连接信息V4.0配置WAN连接参数setPortableHotspotWanInfofun setPortableHotspotWanInfo(params: WanInfoParams): Completable设置便携热点WAN信息配置热点模式的WAN连接getMacCloneBeanfun getMacCloneBean(): MacCloneBean?获取MAC克隆Bean获取MAC克隆配置的详细信息对象wanInfoSourceLiveDataval wanInfoSourceLiveData: MutableLiveData<Resource<WanInfoBean?>>WAN信息源LiveData提供WAN连接信息的响应式数据hotspotWanInfoSourceLiveDataval hotspotWanInfoSourceLiveData: MutableLiveData<Resource<WanInfoBean?>>热点WAN信息源LiveData提供热点WAN信息的响应式数据wanInfoval wanInfo: WanInfoBean?WAN信息Bean当前WAN连接的配置信息对象

  1. OpModeRepository (opModeRepository)

方法名方法签名说明用途getPortableRouterOperationModeInfofun getPortableRouterOperationModeInfo(): Observable<>获取便携路由器操作模式信息获取当前的操作模式配置setPortableRouterOperationModeInfofun setPortableRouterOperationModeInfo(mode: String): Completable设置便携路由器操作模式信息切换路由器的操作模式getHotspotNeedRebootfun getHotspotNeedReboot(): Boolean获取热点是否需要重启判断切换热点模式是否需要设备重启setPortableRouterOperationModeV4Infofun setPortableRouterOperationModeV4Info(params: PortableRouterOpModeV4Params): Observable设置便携路由器操作模式V4信息设置新版本的操作模式,支持无重启切换setUsbTypefun setUsbType(mode: String): Observable<>设置USB类型配置USB连接的设备类型(调制解调器/网络共享)portableOpModeInfoLiveDataval portableOpModeInfoLiveData: LiveData<*>便携操作模式信息LiveData提供操作模式的响应式数据portableRouteModeBeanval portableRouteModeBean: PortableRouteModeBean?便携路由模式Bean当前路由模式的配置对象

  1. ReRepository (reRepository)

方法名方法签名说明用途getMainApInfofun getMainApInfo(): Observable<>获取主AP信息获取主要接入点的配置信息getRepeaterConnInfofun getRepeaterConnInfo(): Observable<>获取中继器连接信息获取中继器模式的连接状态和配置isConnectedViaATAfun isConnectedViaATA(): Boolean检查是否通过ATA连接判断当前是否使用ATA连接方式setRepeaterConnInfofun setRepeaterConnInfo(connInfoList: RepeaterConnInfoList): Observable<*>设置中继器连接信息配置中继器的连接参数和频段信息repeaterConnInfoInfoval repeaterConnInfoInfo: RepeaterConnInfoBean?中继器连接信息Bean当前中继器连接的配置信息对象

总结

通过分析完整的PrInternetConnectionViewModel代码(part1 + part2 + part3),共发现43个接口方法/属性:

PortableRouterRepository: 6个(5个方法,1个属性)

PrInternetConnectionRepository: 16个(13个方法,3个属性)

RouterWanRepository: 10个(7个方法,3个属性)

OpModeRepository: 7个(5个方法,2个属性)

ReRepository: 5个(4个方法,1个属性)

各Part中发现的主要新增方法:

Part 1(基础功能):

数据获取:getSummaryInfo、getConnCheckInfo、getMACCloneInfo

配置设置:setMacClone、setMultiWanInfo、setWanConnInfoV40

LiveData:基础的响应式数据绑定

Part 2(交互控制):

连接控制:doConnectConnection、disconnectConnection

UI状态管理:6个折叠状态相关方法

模式切换:setPortableRouterOperationModeV4Info(支持无重启切换)

网络配置:setNetworkInfo

Part 3(高级功能):

USB类型控制:setUsbType

MAC克隆增强:getMacCloneBean、wanInfo

中继器控制:setRepeaterConnInfo、repeaterConnInfoInfo

核心功能分类:

基础连接管理:WAN连接、热点、USB连接的获取和配置

智能模式切换:支持无重启的操作模式切换和USB类型动态调整

负载均衡与备份:多WAN负载均衡、连接优先级管理

网络监控与检测:连接状态监测、ATA连接检测

UI状态持久化:界面折叠状态、用户交互状态管理

设备兼容性:MAC克隆、中继器配置、频段选择

响应式架构:完整的LiveData响应式数据绑定体系

这个完整的接口设计体现了企业级Android应用的Repository模式最佳实践,通过清晰的职责分离和全面的功能覆盖,为复杂的便携路由器网络管理提供了稳定可靠的架构基础。

// WanInfoBean.kt
package com.tplink.tether.network.tmp.beans.wan

import com.tplink.tether.network.tmp.beans.wan.model.*
import com.tplink.tether.tmp.packet.TMPDefine
import com.tplink.tether.tmp.util.FormatTransfer

class WanInfoBean {
    
    var isVlanSupportable: Boolean = false
    var isVlanEnabled: Boolean = false
    var vlanID: Int = 0
    var isAutoDetectSupportable: Boolean = false
    var isMacClone: Boolean = false
    var clonedMac: String? = null
    var isModifyForbidden: Boolean = false
    var supportableTypeList: MutableList<Byte> = mutableListOf()
    var connectedTypeList: MutableList<Byte> = mutableListOf()
    var connectingTypeList: MutableList<Byte> = mutableListOf()
    var supportWanLanReusePortList: MutableList<Byte> = mutableListOf()
    var contentList: String? = null
    var recommondWanLanReusePortList: MutableList<Byte> = mutableListOf()
    var connectingWanLanReusePort: Byte? = null
    var dynamicIPModel: DynamicIPModel? = null
    var staticIPModel: StaticIPModel? = null
    var pppoeModel: PPPoEModel? = null
    var bigpondCableModel: BigpondCableModel? = null
    var l2TPModel: L2TPModel? = null
    var pptpModel: PPTPModel? = null
    var dsLiteModel: DSLiteModel? = null
    var ipv6PlusMode: Ipv6PlusModel? = null
    var ocnMode: OcnModel? = null
    var onlineDuration: Long? = null
    var isWanLanIptvReuseSupport: Boolean = true
    var isWanLanMacCloneModifySupport: Boolean = false

    companion object {
        /**
         * BE450JP本地深化功能机型指定的拨号方式排序列表
         */
        private val JP_LOCAL_SORTED_CONN_TYPE_LIST = listOf(
            TMPDefine.WAN_CONN_MODE.CONN_TYPE_DYNAMICIP,
            TMPDefine.WAN_CONN_MODE.CONN_TYPE_PPPOE,
            TMPDefine.WAN_CONN_MODE.CONN_TYPE_IPV6_PLUS,
            TMPDefine.WAN_CONN_MODE.CONN_TYPE_IPV6_OPTION,
            TMPDefine.WAN_CONN_MODE.CONN_TYPE_OCN,
            TMPDefine.WAN_CONN_MODE.CONN_TYPE_TRANSIX,
            TMPDefine.WAN_CONN_MODE.CONN_TYPE_XPASS,
            TMPDefine.WAN_CONN_MODE.CONN_TYPE_V6_CONNECT,
            TMPDefine.WAN_CONN_MODE.CONN_TYPE_DS_LITE,
            TMPDefine.WAN_CONN_MODE.CONN_TYPE_BPA,
            TMPDefine.WAN_CONN_MODE.CONN_TYPE_3G4G,
            TMPDefine.WAN_CONN_MODE.CONN_TYPE_STATICIP,
            TMPDefine.WAN_CONN_MODE.CONN_TYPE_L2TP,
            TMPDefine.WAN_CONN_MODE.CONN_TYPE_PPTP
        )
    }

    fun getPortContentList(): List<String> {
        if (contentList.isNullOrEmpty()) {
            return emptyList()
        }
        val oriList = contentList!!.split(";")
        return oriList.map { portName ->
            FormatTransfer.decodeBase64String(portName)
        }
    }

    /**
     * 判断是否是BE450JP本地深化功能机型
     * 判断方式为支持选择拨号方式列表中有JP本地化机型特有字段元素ipv6_option, transix, xpass, v6_connect其中之一,则认为是支持BE450JP本地功能深化机型
     */
    fun isSupportJPLocalConfig(): Boolean {
        if (supportableTypeList.isEmpty()) {
            return false
        }
        return supportableTypeList.any { supportableType ->
            supportableType == TMPDefine.WAN_CONN_MODE.CONN_TYPE_IPV6_OPTION ||
            supportableType == TMPDefine.WAN_CONN_MODE.CONN_TYPE_TRANSIX ||
            supportableType == TMPDefine.WAN_CONN_MODE.CONN_TYPE_XPASS ||
            supportableType == TMPDefine.WAN_CONN_MODE.CONN_TYPE_V6_CONNECT
        }
    }

    /**
     * 获取排序后的拨号方式列表
     */
    fun getSortedConnTypeList(): MutableList<Byte> {
        if (supportableTypeList.isEmpty()) {
            return mutableListOf()
        }
        supportableTypeList.sortBy { getConnTypeSort(it) }
        return supportableTypeList
    }

    /**
     * 获取排序后的BE450JP本地深化功能对应拨号方式的排序位置
     */
    fun getConnTypeSort(wanConnMode: Byte): Int {
        return JP_LOCAL_SORTED_CONN_TYPE_LIST.indexOf(wanConnMode).let {
            if (it == -1) -1 else it
        }
    }
}
    package com.tplink.tether.tether_4_0.component.more.internetconnection.view

import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.ViewModelProvider
import com.tplink.tether.R
import com.tplink.tether.tether_4_0.base.AbstractTetherV4WithDefaultThemeDeprecatedBaseActivity
import com.tplink.tether.tether_4_0.base.networkViewModels
import com.tplink.tether.tether_4_0.component.more.internetconnection.portablerouter.view.ConnectionMethodSelectFragment
import com.tplink.tether.tether_4_0.component.more.internetconnection.portablerouter.view.HotspotContainerFragment
import com.tplink.tether.tether_4_0.component.more.internetconnection.portablerouter.view.PrHotspotConnectionTypeSelectFragment
import com.tplink.tether.tether_4_0.component.more.internetconnection.portablerouter.view.PrHotspotFragment
import com.tplink.tether.tether_4_0.component.more.internetconnection.portablerouter.view.PrRouterUsbFragment
import com.tplink.tether.tether_4_0.component.more.internetconnection.portablerouter.viewmodel.PrInternetConnectionViewModel
import com.tplink.tether.tether_4_0.component.more.internetconnection.viewmodel.Ipv6SettingViewModelV4
import com.tplink.tether.tether_4_0.component.more.internetconnection.viewmodel.SettingConnectionTypeV4ViewModel
import com.tplink.tether.viewmodel.BaseViewModelFactory
import com.tplink.tether.viewmodel.quick_setup.quicksetup_router.QsWanLanReusePortViewModel

class PrSettingInternetConnectionActivity : AbstractTetherV4WithDefaultThemeDeprecatedBaseActivity() {

    companion object {
        const val IS_SWITCH_TO_IPV6 = "_IS_SWITCH_TO_IPV6"
        const val IS_GOTO_PORT_SETTINGS = "_IS_GOTO_PORT_SETTINGS"
        const val IS_HOT_SPOT_SETTING_START = "IS_HOT_SPOT_SETTING_START"

        fun newIntent(
            context: Context,
            isIpv6: Boolean = false,
            isGotoPortSetting: Boolean = false,
            isStartRouterSetting: Boolean = false,
            isStartUsbSetting: Boolean = false,
            isStartHotSpotSetting: Boolean = false
        ): Intent {
            val intent = Intent(context, PrSettingInternetConnectionActivity::class.java)
            intent.putExtra(IS_SWITCH_TO_IPV6, isIpv6)
            intent.putExtra(IS_GOTO_PORT_SETTINGS, isGotoPortSetting)
            intent.putExtra(PrRouterUsbFragment.IS_ROUTER_SETTING_START, isStartRouterSetting)
            intent.putExtra(PrRouterUsbFragment.IS_USB_SETTING_START, isStartUsbSetting)
            intent.putExtra(IS_HOT_SPOT_SETTING_START, isStartHotSpotSetting)
            return intent
        }
    }

    private var currentFragment: Fragment? = null
    private lateinit var ipv6ViewModel: Ipv6SettingViewModelV4
    private lateinit var ipv4ViewModel: SettingConnectionTypeV4ViewModel
    private val qsWanLanReusePortViewModel: QsWanLanReusePortViewModel by lazy {
        ViewModelProvider(this, BaseViewModelFactory(this))
            .get(QsWanLanReusePortViewModel::class.java)
    }
    private lateinit var prSettingViewModel: PrInternetConnectionViewModel
    private var firstFragment: InternetConnectionFragment? = null

    private fun switchFragment(fragment: Fragment) {
        currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
        val fragmentTransaction: FragmentTransaction = supportFragmentManager.beginTransaction()
        fragmentTransaction.setCustomAnimations(
            R.anim.translate_between_interface_right_in,
            R.anim.translate_between_interface_left_out,
            R.anim.translate_between_interface_left_in,
            R.anim.translate_between_interface_right_out
        )
        val tag = fragment.javaClass.canonicalName
        currentFragment?.let {
            fragmentTransaction.hide(it)
        }
        if (fragment.isAdded) {
            fragmentTransaction.show(fragment)
        } else {
            fragmentTransaction.add(R.id.fragment_container, fragment, tag)
        }
        currentFragment = fragment
        fragmentTransaction.addToBackStack(null).commitAllowingStateLoss()
    }

    private fun switchFragment(
        targetFragment: Fragment?,
        tag: String
    ) {//用fragment.replace避免wan/lan自适应选择完模式后menuItem失效
        if (targetFragment != null) {
            supportFragmentManager.beginTransaction()
                .setCustomAnimations(
                    R.anim.translate_between_interface_right_in,
                    R.anim.translate_between_interface_left_out,
                    R.anim.translate_between_interface_left_in,
                    R.anim.translate_between_interface_right_out
                )
                .replace(R.id.fragment_container, targetFragment, tag)
                .addToBackStack(null)
                .commitAllowingStateLoss()
            currentFragment = targetFragment
        }
    }

    private fun addFragment(addFragment: Fragment, removeFragment: Fragment) {
        supportFragmentManager.beginTransaction()
            .remove(removeFragment)
            .add(R.id.fragment_container, addFragment, addFragment.javaClass.simpleName)
            .commitAllowingStateLoss()
    }

    override fun bindView(savedInstanceState: Bundle?) {
        setContentView(R.layout.fragment_common_container)
        ipv4ViewModel = ViewModelProvider(this, BaseViewModelFactory(this))
            .get(SettingConnectionTypeV4ViewModel::class.java)
        ipv6ViewModel = ViewModelProvider(this, BaseViewModelFactory(this))
            .get(Ipv6SettingViewModelV4::class.java)
        prSettingViewModel =
            ViewModelProvider(this, BaseViewModelFactory(this)).get(PrInternetConnectionViewModel::class.java)

        if (intent.getBooleanExtra(PrRouterUsbFragment.IS_ROUTER_SETTING_START, false)) {
            prSettingViewModel.startRouterSetting = true
        } else if (intent.getBooleanExtra(PrRouterUsbFragment.IS_USB_SETTING_START, false)) {
            prSettingViewModel.startUsbSetting = true
        } else if (intent.getBooleanExtra(IS_HOT_SPOT_SETTING_START, false)) {
            prSettingViewModel.startHotSpotSetting = true
        }

        firstFragment = InternetConnectionFragment.newInstance(intent.getBooleanExtra(IS_SWITCH_TO_IPV6, false))
        firstFragment?.let { switchFragment(it) }

        if (intent.getBooleanExtra(IS_GOTO_PORT_SETTINGS, false)) {
            ipv4ViewModel.gotoLanReusePortFragment.postValue(true)
        }
    }

    override fun subscribeViewModel(savedInstanceState: Bundle?) {
        ipv4ViewModel.commonSwitchFragmentEvent.observe(this) {
            when (it) {
                Ipv4DetailFragment.GOTO_PRIMARY_WAN_SETTINGS -> {
                    val fragment = Ipv4SettingFragment.newInstance(0)
                    switchFragment(fragment)
                }
                Ipv4DetailFragment.GOTO_SECONDARY_WAN_SETTINGS -> {
                    val fragment = Ipv4SettingFragment.newInstance(1)
                    switchFragment(fragment)
                }
                WanLanPortModeSelectFragment::class.java.name -> {
                    val fragment = WanLanPortModeSelectFragment.newInstance()
                    switchFragment(fragment, WanLanPortModeSelectFragment.javaClass.simpleName)
                }
                VlanSettingFragment::class.java.name -> {
                    val fragment = VlanSettingFragment.newInstance()
                    switchFragment(fragment)
                }
                WanAdvancedSettingsFragment::class.java.simpleName -> {
                    val fragment = WanAdvancedSettingsFragment.newInstance()
                    switchFragment(fragment)
                }
                PrHotspotConnectionTypeSelectFragment::class.java.name -> {
                    switchFragment(PrHotspotConnectionTypeSelectFragment.newInstance())
                }
            }
        }

        ipv6ViewModel.gotoIpv6SettingFragment.observe(this) {
            val fragment = Ipv6SettingFragment.newInstance()
            switchFragment(fragment)
        }

        ipv6ViewModel.commonSwitchFragmentEvent.observe(this) {
            when(it) {
                ConnectionMethodSelectFragment::class.java.name -> {
                    switchFragment(ConnectionMethodSelectFragment.newInstance())
                }
            }
        }

        ipv4ViewModel.gotoIpv4TypeSelectFragment.observe(this) {
            val fragment = ConnectionTypeSelectFragment.newInstance(
                it,
                isFirstConnectType = true,
                isIPv6ConnectType = false
            )
            switchFragment(fragment)
        }

        ipv4ViewModel.gotoIpv4SecondTypeSelectFragment.observe(this) {
            val fragment = ConnectionTypeSelectFragment.newInstance(
                it,
                isFirstConnectType = false,
                isIPv6ConnectType = false
            )
            switchFragment(fragment)
        }

        ipv6ViewModel.gotoIpv6TypeSelectFragment.observe(this) {
            val fragment = ConnectionTypeSelectFragment.newInstance(
                it,
                isFirstConnectType = false,
                isIPv6ConnectType = true
            )
            switchFragment(fragment)
        }

        ipv6ViewModel.gotoDNSAddressFragment.observe(this) {
            val fragment = DNSAddressFragment.newInstance(it)
            switchFragment(fragment)
        }

        ipv6ViewModel.gotoIpv6AddressSelectFragment.observe(this) {
            val fragment = GetIpv6AddressFragment.newInstance(true, it)
            switchFragment(fragment)
        }

        ipv6ViewModel.gotoIpv6AssignedTypeSelectFragment.observe(this) {
            val fragment = AssignedTypeFragment.newInstance(it)
            switchFragment(fragment)
        }

        ipv6ViewModel.refresh.observe(this) {
            ipv6ViewModel.refresh1.value = it
        }

        ipv4ViewModel.gotoLanReusePortFragment.observe(this) {
            if (qsWanLanReusePortViewModel.isSupportAutoInternetPort()) {
                qsWanLanReusePortViewModel.isFromInternetConnection = true
                qsWanLanReusePortViewModel.tempIsAutoInternetMode = qsWanLanReusePortViewModel.isAutoInternetPort
                val fragment =
                    WanLanPortSupportAutoInternetPortFragment.newInstance(ipv4ViewModel.wanLanReusePortValue)
                switchFragment(
                    fragment,
                    WanLanPortSupportAutoInternetPortFragment.javaClass.simpleName
                )
            } else {
                val fragment =
                    WanLanReusePortFragment.newInstance(ipv4ViewModel.wanLanReusePortValue)
                switchFragment(fragment)
            }
        }

        ipv4ViewModel.commonBack.observe(this) {
            supportFragmentManager.popBackStack()
        }

        ipv6ViewModel.commonBack.observe(this) {
            supportFragmentManager.popBackStack()
        }

        prSettingViewModel.gotoPrRouterUsbFragment.observe(this) {
            val fragment = PrRouterUsbFragment.newInstance()
            switchFragment(fragment, PrRouterUsbFragment::class.java.simpleName)
        }

        prSettingViewModel.gotoPrHotspotFragment.observe(this) {
            val fragment = HotspotContainerFragment.newInstance()
            switchFragment(fragment, HotspotContainerFragment::class.java.simpleName)
        }

        prSettingViewModel.gotoPrRouterUsbFragmentFromDashboard.observe(this) {
            val fragment = PrRouterUsbFragment.newInstance(
                startUsbSetting = prSettingViewModel.startUsbSetting,
                startRouterSetting = prSettingViewModel.startRouterSetting
            )
            firstFragment?.let {
                addFragment(fragment, it)
            }
        }

        prSettingViewModel.gotoPrHotspotFragmentFromDashboard.observe(this) {
            val fragment = HotspotContainerFragment.newInstance()
            firstFragment?.let {
                addFragment(fragment, it)
            }
        }
    }
}
          
        
            
            
            
                
         

   
// WanIPBean.kt
package com.tplink.tether.network.tmp.beans

import com.google.gson.annotations.JsonAdapter
import com.google.gson.annotations.SerializedName
import com.tplink.libtputility.tlv.annotation.TLVType
import com.tplink.tether.network.gson.adapter.JsonAdapters
import com.tplink.tether.tmp.packet.TMPDefine

open class WanIPBean {
    @JsonAdapter(JsonAdapters.IpFormatAdapter::class)
    @TLVType(
        value = TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_IP,
        alternate = [TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_DSL_WAN_IP]
    )
    protected var ip: Int = -1

    @JsonAdapter(JsonAdapters.IpFormatAdapter::class)
    @TLVType(
        value = TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_SUBNET_MASK,
        alternate = [TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_DSL_WAN_SUBNET_MASK]
    )
    @SerializedName("subnet_mask")
    protected var subnetMask: Int = 0

    @JsonAdapter(JsonAdapters.IpFormatAdapter::class)
    @TLVType(
        value = TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_GATEWAY,
        alternate = [TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_DSL_WAN__GATEWAY]
    )
    protected var gateway: Int = 0

    @TLVType(TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_MTU)
    protected var mtu: Int = 0

    @JsonAdapter(JsonAdapters.IpFormatAdapter::class)
    @TLVType(
        value = TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_M_DNS,
        alternate = [TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_DSL_WAN_M_DNS]
    )
    @SerializedName("primary_dns")
    protected var primaryDns: Int = 0

    @JsonAdapter(JsonAdapters.IpFormatAdapter::class)
    @TLVType(
        value = TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_S_DNS,
        alternate = [TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_DSL_WAN_S_DNS]
    )
    @SerializedName("secondary_dns")
    protected var secondaryDns: Int = 0

    fun getIp(): Int {
        return if (ip == -1) 0 else ip
    }

    // 仅用来标识设备端是否有传过来ip,之所以需要是因为之前其它地方有使用到ip,若贸然将ip类型改成Integer,
    // 其它地方需要在使用前做判断,故仅修改默认值标识设备端是否有传递,没有则为-1,有则为其它值
    fun getActuallyIp(): Int = ip

    fun setIp(ip: Int) {
        this.ip = ip
    }

    fun getSubnetMask(): Int = subnetMask
    fun setSubnetMask(subnetMask: Int) {
        this.subnetMask = subnetMask
    }

    fun getGateway(): Int = gateway
    fun setGateway(gateway: Int) {
        this.gateway = gateway
    }

    fun getMtu(): Int = mtu
    fun setMtu(mtu: Int) {
        this.mtu = mtu
    }

    fun getPrimaryDns(): Int = primaryDns
    fun setPrimaryDns(primaryDns: Int) {
        this.primaryDns = primaryDns
    }

    fun getSecondaryDns(): Int = secondaryDns
    fun setSecondaryDns(secondaryDns: Int) {
        this.secondaryDns = secondaryDns
    }
}

// L2TPModel.kt
package com.tplink.tether.network.tmp.beans.wan.model

import com.tplink.libtputility.tlv.annotation.TLVStructure
import com.tplink.libtputility.tlv.annotation.TLVType
import com.tplink.tether.network.tmp.beans.WanIPBean
import com.tplink.tether.tmp.packet.TMPDefine

@TLVStructure
data class L2TPModel(
    @TLVType(TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_UNAME)
    var username: String? = null,

    @TLVType(TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_PSWD)
    var password: String? = null,

    @TLVType(TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_SERVER_IP)
    var serverIp: String? = null,

    @TLVType(TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_INTERNET_IP)
    var internetIp: Int = 0,

    @TLVType(TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_INTERNET_DNS)
    var internetDns: Int = 0,

    @TLVType(TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_MAX_IDLE_TIME)
    var maxIdleTime: Int = 0,

    @TLVType(TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_IP_MODE)
    var ipMode: Byte = 0, // 0 is dynamic, 1 is static IP

    @TLVType(TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_CONNECT_MODE)
    var connectMode: Byte = 0
) : WanIPBean()

// PPTPModel.kt
package com.tplink.tether.network.tmp.beans.wan.model

import com.tplink.libtputility.tlv.annotation.TLVStructure
import com.tplink.libtputility.tlv.annotation.TLVType
import com.tplink.tether.network.tmp.beans.WanIPBean
import com.tplink.tether.tmp.packet.TMPDefine

@TLVStructure
data class PPTPModel(
    @TLVType(TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_UNAME)
    var username: String? = null,

    @TLVType(TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_PSWD)
    var password: String? = null,

    @TLVType(TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_SERVER_IP)
    var serverIp: String? = null,

    @TLVType(TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_INTERNET_IP)
    var internetIp: Int = 0,

    @TLVType(TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_INTERNET_DNS)
    var internetDns: Int = 0,

    // 注意:这个字段没有@TLVType注解,保持与原Java代码一致
    var maxIdleTime: Int = 0,

    @TLVType(TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_IP_MODE)
    var ipMode: Byte = 0, // 0 is dynamic, 1 is static IP

    @TLVType(TMPDefine.TMP_PAYLOAD_TYPE.TMP_TYPE_WAN_CONNECT_MODE)
    var connectMode: Byte = 0
) : WanIPBean()

抱歉,我明白了。按照你要求的四个功能分类重新整理:

功能分类表格

功能分类具体功能点简要需求分析Mercusys app是否需要支持Tether app是否需要支持

  1. IPv4信息展示

| Backup Mode IPv4信息展示 | Internet Connection页面Backup模式展示 | 1. 只展示In Use的连接方式Connected小卡,连接状态:In Use、Idle、No Internet
2. 不同连接方式信息展示:Ethernet显示拨号信息、USB显示Location/ISP/APN/Dial Number、Hotspot显示Wi-Fi信息
3. 连接卡片支持折叠/展开,用户折叠后记住操作 | 是 | 是 | | Load Balance Mode IPv4信息展示 | Internet Connection页面Load Balance模式展示 | 1. 展示In Use的所有连接方式,按流量大小排序显示卡片
2. 流量百分比显示在卡片右上角(如"60% Traffic")
3. 流量为0(即没有InUse)的连接不显示
4. 支持连接卡片折叠/展开,默认初始为全部折叠状态 | 是 | 是 |

  1. IPv4配置管理

| Internet Mode管理 | Internet Settings页面模式配置 | 1. Internet Mode显示当前模式(Backup/Load Balance),可点击切换
2. Multi-WAN开关控制,开启前需要选择preferred方式
3. 提供"Set Preferred"快速配置链接 | 是 | 是 | | Backup模式配置 | 连接优先级管理 | 1. Connection Methods显示preferred/backup标识
2. 排序实时变动:preferred > backup1 > backup2
3. 支持拖拽调整连接方式顺序,采用模态弹窗显示
4. 未保存返回需要二次确认弹窗 | 是 | 是 | | Load Balance模式配置 | 流量权重管理 | 1. 显示流量权重实时分配,Connection Methods按流量多少排序
2. Traffic Allocation Weight采用滑块界面,支持0-10档位调整
3. 权重分配公式:a/(a+b+c)*100%
4. 约束检查:三种连接全部调至0时报错,不能保存 | 是 | 是 | | Ethernet IPv4配置 | Ethernet连接详细设置 | 1. 支持多种连接类型:Dynamic IP、Static IP、PPPoE、L2TP、PPTP
2. Static IP配置:IP Address、Subnet Mask、Default Gateway(Required),Primary DNS(Required)、Secondary DNS(Optional)
3. Auto Detect按钮和使用建议
4. 显示连接状态和优先级状态(只读) | 是 | 是 | | USB Internet IPv4配置 | USB设备连接配置 | 1. Plugged-In Device设备类型识别:3/4G Modem或Phone/Tablet
2. 3/4G Modem配置:Location、Mobile ISP自动配置,或Manual Setup手动配置
3. Manual Setup包含:Dial Number(Required)、APN、User Name(Optional)、Password(Optional)
4. MTU Size配置(默认1480 Bytes),Use Custom DNS Server开关 | 是 | 是 | | USB连接故障处理 | Tethering连接问题处理 | 1. 连接失败弹窗提供详细故障排除步骤
2. "How to connect mobile device?"提供指导
3. 分iOS/Android提供具体操作步骤 | 是 | 是 | | Hotspot IPv4配置 | Hotspot连接详细设置 | 1. 显示Host Network Name(%SSID%)、Band(5 GHz)、Front-End AP(Auto)
2. Connection Type支持Dynamic IP和Static IP
3. 连接状态:Connected、No Host Wi-Fi等
4. Lock to AP功能支持,前端AP选择 | 是 | 是 | | Advanced Settings配置 | 高级功能设置 | 1. Connection Check:三种方式单独设置,Track Sensitivity预设和Custom配置
2. MAC Clone:Ethernet和Hotspot分开设置,冲突检测和二次确认
3. Ethernet Ports:WAN端口选择,Turn all ports to LAN功能
4. 其他高级功能:Flow Controller、NAT、LAN Settings等 | 是 | 是 |

  1. IPv6信息展示

| IPv6连接状态展示 | Internet Connection页面IPv6标签展示 | 1. IPv6标签页独立显示,未开启时显示Disabled
2. 显示IPv6地址、Primary DNS、Secondary DNS等连接信息
3. 支持与IPv4标签页切换显示
4. IPv6 LAN信息展示:Assigned Type、Address Prefix、IP Address等 | 是 | 是 |

  1. IPv6配置管理

| IPv6连接配置 | IPv6 Internet Settings配置 | 1. 需要选择一种连接方式启用IPv6:Ethernet、USB Internet、Hotspot
2. 选中选项后自动返回,默认不选择任何方式
3. IPv6 Internet Settings配置入口,新增Connection Method字段显示 | 是 | 是 | | IPv6详细设置 | IPv6连接方式详细配置 | 1. Connection Method选择:Ethernet(主要)、USB Internet、Hotspot
2. Connection Type:Static IP、Dynamic IP(SLAAC/HDCPv6)
3. IPv6 LAN配置:Assigned Type为SLAAC+Stateless DHCP
4. Address Prefix设置(/64),MTU Size(默认1500 bytes)
5. 支持IPv6地址、Default Gateway、Primary/Secondary DNS配置 | 是 | 是 |

现在严格按照IPv4信息展示、IPv4配置管理、IPv6信息展示、IPv6配置管理这四个分类整理完成。

现在基于这个超过2000行的ViewModel代码,我可以完善分层架构设计了:

  1. 分层架构设计

3.1 模块划分

Internet Connection模块分为视图层、处理层和数据访问层。

视图层提供网络连接配置的展示和功能修改入口,主要包括Internet Settings主页、以太网配置页、USB网络配置页、热点配置页、网络模式切换页、连接优先级设置页、高级设置配置页等。

处理层主要处理来自视图层的配置请求和来自数据访问层的网络状态数据,进行业务逻辑处理后返回给视图层。包括连接状态管理、Multi-WAN配置、流量分配计算、连接检测逻辑等核心业务。

数据访问层主要处理与设备端的网络配置交互以及本地缓存的连接状态信息。

接入WANLAN SDK后,SDK负责处理视图层和处理层,与设备端的数据交互及本地缓存需开放接口让各APP进行适配。

3.2 层级划分

页面结构划分如下图所示:

┌─────────────────────────────────────────────────────────────────────┐ │ 视图层 │ ├─────────────────────────────────────────────────────────────────────┤ │ PrInternetSettingActivity │ │ │ │ │ ├──nav:nav_pr_router_usb──────┐ │ │ │ │ │ │ PrEthernetFragment PrUsbFragment PrHotspotFragment │ │ │ │ │ ┌──────┴─────────────┐ ┌───────┴─────────────┐ ┌───────┴──────────┐ │ ConnectionPrioritySheet │ TetheringConnectFail │ HotSpotWifiSheet │ │ TrafficAllocationWeight │ PrConnectGuideSheet │ ConnectionPrioritySheet│ │ GuideFragment │ PrShareDeviceSheet │ TrafficAllocation... │ ├─────────────────────────────────────────────────────────────────────┤ │ ↑ 发起请求 ↓ 通过LiveData反馈数据 │ ├─────────────────────────────────────────────────────────────────────┤ │ 业务逻辑层 │ ├─────────────────────────────────────────────────────────────────────┤ │ PrInternetConnectionViewModel (2000+行代码) │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ 核心业务功能模块: │ │ │ │ • refreshData() - 数据刷新管理 │ │ │ │ • updateTmpMultiWanSetInfo() - Multi-WAN状态管理 │ │ │ │ • modifyEnable() - 连接开关控制 (V1/V2兼容) │ │ │ │ • setMultiWan() - Multi-WAN配置 │ │ │ │ • modifyPriority() - 连接优先级管理 │ │ │ │ • modifyWeight() - 流量权重分配 │ │ │ │ • setConnCheckInfo() - 连接检测配置 │ │ │ │ • addEthernetInfoForLoadBalance() - 负载均衡信息管理 │ │ │ │ • switchOperationMode() - 操作模式切换 │ │ │ │ • setWanConnInfo()/setHotspotWanInfo() - WAN连接配置 │ │ │ └─────────────────────────────────────────────────────────────────┘ │ ├─────────────────────────────────────────────────────────────────────┤ │ ↓ 执行请求 ↑ 返回数据 │ ├─────────────────────────────────────────────────────────────────────┤ │ 数据层 │ ├─────────────────────────────────────────────────────────────────────┤ │ ┌─────────────────────┐ ┌─────────────────────┐ ┌──────────────────┐ │ │ │PrInternetConnection │ │PortableRouter │ │RouterWan │ │ │ │Repository │ │Repository │ │Repository │ │ │ │• setMultiWanInfo │ │• getSummaryInfo │ │• getWanConnInfo │ │ │ │• setConnCheckInfo │ │• setNetworkInfo │ │• setWanConnInfo │ │ │ │• getUsbWanInfo │ │• doConnectConnection│ │• getMACCloneInfo │ │ │ │• setMacClone │ │• disconnectConnection││• setPortableHotspot│ │ │ └─────────────────────┘ └─────────────────────┘ └──────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ 数据模型 (BO/Bean): │ │ │ │ • MultiWanInfo - Multi-WAN配置信息 │ │ │ │ • ConnectionCheckInfo - 连接检测信息 │ │ │ │ • LoadBalanceWanInfo - 负载均衡连接信息 │ │ │ │ • PortableUsbWanInfo - USB WAN信息 │ │ │ │ • MacCloneInfo - MAC克隆信息 │ │ │ └─────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────┘

架构特点分析:

单一ViewModel设计:PrInternetConnectionViewModel作为超级控制器,管理所有Internet Connection相关的业务逻辑

V1/V2版本兼容:通过isSupportInternetConnectionV2()进行版本判断,同时支持旧版和新版API

响应式数据流:大量使用LiveData进行状态管理和UI更新

Repository分离:按功能将数据访问分为多个Repository

复杂状态管理:包含Multi-WAN、Load Balance、连接检测等复杂网络状态的统一管理

这个架构非常复杂,需要在SDK化时进行模块拆分和职责分离。