-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* observe state of metered network. * use registerNetworkCallback * observe state of metered network. * explicitly fallback on macos * show option only when supported * fix * don't hold context * 在调试设置中增加计费网络信息 * Add AndroidMeteredNetworkDetector logs * Add NoopMeteredNetworkDetector * fix ios package name --------- Co-authored-by: Him188 <[email protected]>
- Loading branch information
1 parent
f975b9d
commit 3c1875a
Showing
13 changed files
with
321 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
86 changes: 86 additions & 0 deletions
86
app/shared/app-platform/src/androidMain/kotlin/platform/MeteredNetworkDetector.android.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
/* | ||
* Copyright (C) 2024 OpenAni and contributors. | ||
* | ||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. | ||
* Use of this source code is governed by the GNU AGPLv3 license, which can be found at the following link. | ||
* | ||
* https://github.com/open-ani/ani/blob/main/LICENSE | ||
*/ | ||
|
||
package me.him188.ani.app.platform | ||
|
||
import android.annotation.SuppressLint | ||
import android.net.ConnectivityManager | ||
import android.net.Network | ||
import android.net.NetworkCapabilities | ||
import android.net.NetworkRequest | ||
import kotlinx.coroutines.flow.Flow | ||
import kotlinx.coroutines.flow.MutableStateFlow | ||
import me.him188.ani.utils.logging.logger | ||
|
||
|
||
@SuppressLint("MissingPermission") | ||
private class AndroidMeteredNetworkDetector( | ||
context: Context | ||
) : MeteredNetworkDetector { | ||
private val connectivityManager = context.getSystemService(ConnectivityManager::class.java) | ||
private val logger by lazy { logger<AndroidMeteredNetworkDetector>() } | ||
|
||
private val flow = MutableStateFlow(getCurrentIsMetered()) | ||
override val isMeteredNetworkFlow: Flow<Boolean> get() = flow | ||
|
||
// Create a NetworkCallback to detect network changes | ||
private val networkCallback = object : ConnectivityManager.NetworkCallback() { | ||
override fun onAvailable(network: Network) { // 连接 WiFi | ||
flow.tryEmit(getCurrentIsMetered()) | ||
} | ||
|
||
override fun onLost(network: Network) { // 断开 WiFi | ||
flow.tryEmit(getCurrentIsMetered()) | ||
} | ||
|
||
// WiFi 设置变更 (设置为计费网络) | ||
// 连接/断开 WiFi 不会触发 onCapabilitiesChanged | ||
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) { | ||
val isMetered = !networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) | ||
log { "onCapabilitiesChanged: isMetered=$isMetered" } | ||
flow.tryEmit(isMetered) | ||
} | ||
} | ||
|
||
init { | ||
// Register the NetworkCallback instead of using BroadcastReceiver | ||
val networkRequest = NetworkRequest.Builder() | ||
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) | ||
.build() | ||
connectivityManager.registerNetworkCallback(networkRequest, networkCallback) | ||
|
||
// Emit the first value | ||
flow.tryEmit(getCurrentIsMetered()) | ||
} | ||
|
||
private fun getCurrentIsMetered(): Boolean { | ||
val activeNetwork = connectivityManager.activeNetwork ?: return false | ||
val activeNetworkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork) ?: return false | ||
|
||
// Return whether the network is metered or not | ||
val isMetered = !activeNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) | ||
log { "getCurrentIsMetered: isMetered=$isMetered" } | ||
return isMetered | ||
} | ||
|
||
override fun dispose() { | ||
// Unregister the network callback when no longer needed | ||
connectivityManager.unregisterNetworkCallback(networkCallback) | ||
} | ||
|
||
private inline fun log(message: () -> String) { | ||
if (BuildConfig.DEBUG) { | ||
logger.debug(message()) | ||
} | ||
} | ||
} | ||
|
||
actual fun createMeteredNetworkDetector(context: Context): MeteredNetworkDetector { | ||
return AndroidMeteredNetworkDetector(context) | ||
} |
37 changes: 37 additions & 0 deletions
37
app/shared/app-platform/src/commonMain/kotlin/platform/MeteredNetworkDetector.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/* | ||
* Copyright (C) 2024 OpenAni and contributors. | ||
* | ||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. | ||
* Use of this source code is governed by the GNU AGPLv3 license, which can be found at the following link. | ||
* | ||
* https://github.com/open-ani/ani/blob/main/LICENSE | ||
*/ | ||
|
||
package me.him188.ani.app.platform | ||
|
||
import kotlinx.coroutines.flow.Flow | ||
import kotlinx.coroutines.flow.flowOf | ||
|
||
/** | ||
* Platform-dependent network type detector. | ||
* | ||
* Use [createMeteredNetworkDetector] to create instance. | ||
*/ | ||
interface MeteredNetworkDetector { | ||
val isMeteredNetworkFlow: Flow<Boolean> | ||
|
||
/** | ||
* Dispose listeners or callbacks which may be created at initializing detector. | ||
*/ | ||
fun dispose() | ||
} | ||
|
||
object NoopMeteredNetworkDetector : MeteredNetworkDetector { | ||
override val isMeteredNetworkFlow: Flow<Boolean> = flowOf(false) | ||
|
||
override fun dispose() { | ||
} | ||
} | ||
|
||
|
||
expect fun createMeteredNetworkDetector(context: ContextMP): MeteredNetworkDetector |
107 changes: 107 additions & 0 deletions
107
app/shared/app-platform/src/desktopMain/kotlin/platform/MeteredNetworkDetector.desktop.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
/* | ||
* Copyright (C) 2024 OpenAni and contributors. | ||
* | ||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. | ||
* Use of this source code is governed by the GNU AGPLv3 license, which can be found at the following link. | ||
* | ||
* https://github.com/open-ani/ani/blob/main/LICENSE | ||
*/ | ||
|
||
package me.him188.ani.app.platform | ||
|
||
import com.sun.jna.platform.win32.COM.COMException | ||
import com.sun.jna.platform.win32.COM.COMUtils | ||
import com.sun.jna.platform.win32.COM.Unknown | ||
import com.sun.jna.platform.win32.Guid | ||
import com.sun.jna.platform.win32.Ole32 | ||
import com.sun.jna.platform.win32.WTypes | ||
import com.sun.jna.platform.win32.WinNT.HRESULT | ||
import com.sun.jna.ptr.IntByReference | ||
import com.sun.jna.ptr.PointerByReference | ||
import kotlinx.coroutines.flow.Flow | ||
import kotlinx.coroutines.flow.flow | ||
import me.him188.ani.utils.logging.logger | ||
import me.him188.ani.utils.logging.warn | ||
import me.him188.ani.utils.platform.Platform | ||
import me.him188.ani.utils.platform.currentPlatformDesktop | ||
|
||
|
||
private class WindowsMeteredNetworkDetector : MeteredNetworkDetector { | ||
private val logger = logger<MeteredNetworkDetector>() | ||
|
||
private val flow = flow { | ||
while (true) { | ||
emit(getIsMetered()) | ||
kotlinx.coroutines.delay(60000) | ||
} | ||
} | ||
override val isMeteredNetworkFlow: Flow<Boolean> = flow | ||
|
||
private fun getIsMetered(): Boolean { | ||
var networkCostManager: INetworkCostManager? = null | ||
|
||
try { | ||
val coInitializeHResult = Ole32.INSTANCE.CoInitializeEx(null, Ole32.COINIT_MULTITHREADED) | ||
COMUtils.checkRC(coInitializeHResult) | ||
|
||
val pNetworkCostManager = PointerByReference() | ||
val coCreateInstanceHResult = Ole32.INSTANCE.CoCreateInstance( | ||
CLSID_NetworkListManager, | ||
null, | ||
WTypes.CLSCTX_ALL, | ||
IID_INetworkCostManager, | ||
pNetworkCostManager, | ||
) | ||
COMUtils.checkRC(coCreateInstanceHResult) | ||
|
||
networkCostManager = INetworkCostManager(pNetworkCostManager) | ||
val pCost = IntByReference() | ||
|
||
val getCostResult = networkCostManager.GetCost(pCost) | ||
COMUtils.checkRC(getCostResult) | ||
|
||
return (pCost.value and NLM_CONNECTION_COST_FIXED) != 0 | ||
} catch (ex: COMException) { | ||
logger.warn(ex) { "Failed to get network status." } | ||
return false | ||
} finally { | ||
networkCostManager?.Release() | ||
Ole32.INSTANCE.CoUninitialize() | ||
} | ||
} | ||
|
||
override fun dispose() { | ||
|
||
} | ||
|
||
private class INetworkCostManager(pointer: PointerByReference) : Unknown(pointer.value) { | ||
@Suppress("FunctionName") | ||
fun GetCost(cost: IntByReference): HRESULT { | ||
return _invokeNativeObject(3, arrayOf(pointer, cost, null), HRESULT::class.java) as HRESULT | ||
} | ||
} | ||
|
||
@Suppress("unused") | ||
companion object { | ||
private val CLSID_NetworkListManager = Guid.CLSID("{DCB00C01-570F-4A9B-8D69-199FDBA5723B}") | ||
private val IID_INetworkCostManager = Guid.IID("{DCB00008-570F-4A9B-8D69-199FDBA5723B}") | ||
|
||
private const val NLM_CONNECTION_COST_UNKNOWN = 0 | ||
private const val NLM_CONNECTION_COST_UNRESTRICTED = 0x1 | ||
private const val NLM_CONNECTION_COST_FIXED = 0x2 | ||
private const val NLM_CONNECTION_COST_VARIABLE = 0x4 | ||
private const val NLM_CONNECTION_COST_OVERDATALIMIT = 0x10000 | ||
private const val NLM_CONNECTION_COST_CONGESTED = 0x20000 | ||
private const val NLM_CONNECTION_COST_ROAMING = 0x40000 | ||
private const val NLM_CONNECTION_COST_APPROACHINGDATALIMIT = 0x80000 | ||
} | ||
} | ||
|
||
actual fun createMeteredNetworkDetector(context: Context): MeteredNetworkDetector { | ||
return when (currentPlatformDesktop()) { | ||
is Platform.Windows -> WindowsMeteredNetworkDetector() | ||
is Platform.MacOS -> NoopMeteredNetworkDetector // macos API 要用 swift 才能实现 | ||
is Platform.Linux -> NoopMeteredNetworkDetector | ||
} | ||
} | ||
|
14 changes: 14 additions & 0 deletions
14
app/shared/app-platform/src/iosMain/kotlin/platform/MeteredNetworkDetector.ios.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/* | ||
* Copyright (C) 2024 OpenAni and contributors. | ||
* | ||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. | ||
* Use of this source code is governed by the GNU AGPLv3 license, which can be found at the following link. | ||
* | ||
* https://github.com/open-ani/ani/blob/main/LICENSE | ||
*/ | ||
|
||
package me.him188.ani.app.platform | ||
|
||
actual fun createMeteredNetworkDetector(context: Context): MeteredNetworkDetector { | ||
return NoopMeteredNetworkDetector | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.