Skip to content

Commit

Permalink
Merge pull request #9 from rommansabbir/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
rommansabbir authored Nov 5, 2021
2 parents 4db95fa + 701c409 commit a89d54d
Show file tree
Hide file tree
Showing 12 changed files with 377 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.rommansabbir.networkx

data class LastKnownSpeed(val speed: String, val networkTypeNetwork: String, val simplifiedSpeed : String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.rommansabbir.networkx

object NetworkSpeedType {
const val GB = "GB"
const val MB = "MB"
const val KB = "KB"
}
45 changes: 45 additions & 0 deletions NetworkX/src/main/java/com/rommansabbir/networkx/NetworkXConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.rommansabbir.networkx

import android.app.Application
import com.rommansabbir.networkx.NetworkXConfig.Builder
import com.rommansabbir.networkx.exceptions.NetworkXNotInitializedException

/**
* This class represent the configurations for NetworkX.
*
* Use the [Builder] class to initialize [NetworkXConfig].
*
* @param application, [Application] reference to initialize NetworkX properly
* @param enableSpeedMeter, determine if to enable monitoring network speed
*/
class NetworkXConfig private constructor(
val application: Application,
val enableSpeedMeter: Boolean
) {
// Use the builder class to initialize NetworkXConfig
class Builder {
// States
private lateinit var application: Application
private var isSpeedMeterEnabled: Boolean = false

// Hold application reference
fun withApplication(application: Application): Builder {
this.application = application
return this
}

// Hold enable speed meter status reference
fun withEnableSpeedMeter(value: Boolean): Builder {
this.isSpeedMeterEnabled = value
return this
}

// Return an new instance of NetworkXConfig
fun build(): NetworkXConfig {
if (!this::application.isInitialized) {
throw NetworkXNotInitializedException()
}
return NetworkXConfig(application, isSpeedMeterEnabled)
}
}
}
104 changes: 93 additions & 11 deletions NetworkX/src/main/java/com/rommansabbir/networkx/NetworkXManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@ import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log

internal class NetworkXManager constructor(private val application: Application) {
internal class NetworkXManager constructor(
private val application: Application,
private val isSpeedMeterEnabled: Boolean
) {
// Callback for activity lifecycle for this specific application
private val activityCallback = object : Application.ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
Expand All @@ -18,22 +24,34 @@ internal class NetworkXManager constructor(private val application: Application)

override fun onActivityResumed(activity: Activity) {
try {
getConnectivityManager(application).registerNetworkCallback(
getNetworkRequest(),
getNetworkCallBack
)
}
catch (e : Exception){
getConnectivityManager(application).apply {
registerNetworkCallback(
getNetworkRequest(),
getNetworkCallBack
)
if (isSpeedMeterEnabled) {
enabledSpeedMeter()
logThis("onActivityResumed: speed meter enabled")
}
logThis("onActivityResumed: listener registered")
}
} catch (e: Exception) {
e.printStackTrace()
logThis(e.message)
}
}

override fun onActivityPaused(activity: Activity) {
try {
getConnectivityManager(application).unregisterNetworkCallback(getNetworkCallBack)
}
catch (e : Exception){
logThis("onActivityPaused: listener unregistered")
if (isSpeedMeterEnabled) {
disableSpeedMeter()
logThis("onActivityResumed: speed meter disabled")
}
} catch (e: Exception) {
e.printStackTrace()
logThis(e.message)
}
}

Expand All @@ -45,16 +63,42 @@ internal class NetworkXManager constructor(private val application: Application)

}

@Volatile
private var trafficUtils: TrafficUtils? = null

// Initialize the Manager
init {
try {
getConnectivityManager(application)
application.registerActivityLifecycleCallbacks(activityCallback)
}
catch (e : Exception){
trafficUtils = TrafficUtils()
} catch (e: Exception) {
e.printStackTrace()
}
}

private val mInterval = 1000
private var mHandler: Handler? = null

//Monitor network speed in a loop
private val runnableCode: Runnable = object : Runnable {
override fun run() {
try {
trafficUtils?.getNetworkSpeed {
NetworkXProvider.setNetworkSpeed(it)
logThis("Internet speed: $it")
} ?: run {
logThis("Traffic Utils instance is null")
}
mHandler?.postDelayed(this, mInterval.toLong())
} catch (e: Exception) {
e.printStackTrace()
logThis(e.message)
}
}
}


// Get ConnectivityManager which is a system service
private fun getConnectivityManager(application: Application) =
application.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
Expand All @@ -69,6 +113,7 @@ internal class NetworkXManager constructor(private val application: Application)
.addTransportType(NetworkCapabilities.TRANSPORT_VPN)
.build()
}

// Callback to get notified about network availability
private val getNetworkCallBack = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
Expand All @@ -81,4 +126,41 @@ internal class NetworkXManager constructor(private val application: Application)
NetworkXProvider.setConnection(false)
}
}

// Disable current network speed monitoring
private fun disableSpeedMeter() {
try {
trafficUtils?.releaseScopes()
if (mHandler != null) {
mHandler!!.removeCallbacks(runnableCode)
mHandler = null
logThis("Callback removed, Handler instance is null")
}
} catch (e: Exception) {
e.printStackTrace()
logThis(e.message)
}
}

// Enable current network speed monitoring
private fun enabledSpeedMeter() {
try {
trafficUtils?.enabledScopes()
if (mHandler != null) {
mHandler!!.removeCallbacks(runnableCode)
mHandler = null
logThis("Callback removed, Handler instance is null")
}
mHandler = Handler(Looper.getMainLooper())
mHandler!!.post(runnableCode)
logThis("OnNetwork Update - Callback added with Handler new instance")
} catch (e: Exception) {
e.printStackTrace()
logThis(e.message)
}
}

private fun logThis(log: String?) {
Log.d("NetworkX:", log ?: "Something went wrong")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,62 @@ object NetworkXProvider {
get() = isInternetConnectedMutableLiveData


// Store last known speed
private var lastKnownSpeedRef: LastKnownSpeed? = null

/**
* Public access point to get the last known network speed
*/
val lastKnownSpeed: LastKnownSpeed?
get() = lastKnownSpeedRef

private val lastKnownSpeedLiveDataRef: MutableLiveData<LastKnownSpeed> = MutableLiveData(null)

/**
* Public access point to get the last known network speed which can be observed from Activity/Fragment.
*/
val lastKnownSpeedLiveData: LiveData<LastKnownSpeed>
get() = lastKnownSpeedLiveDataRef

fun setNetworkSpeed(value: LastKnownSpeed) {
synchronized(value) {
lastKnownSpeedRef = value
lastKnownSpeedLiveDataRef.value = value
}
}

/**
* Main entry point for the client.
* First check for [NetworkXManager] instance, if the status is null then initialize it properly
* else ignore the initialization.
*
* @param application, [Application] reference
*/
fun init(application: Application) {
@Deprecated("Use NetworkXProvider.enable(config: NetworkXConfig) to initialize NetworkX properly")
fun init(application: Application, enableSpeedMeter: Boolean = false) {
try {
if (manager != null) {
return
}
manager = NetworkXManager(application, enableSpeedMeter)
} catch (e: Exception) {
throw e
}
}

/**
* Main entry point for the client.
* First check for [NetworkXManager] instance, if the status is null then initialize it properly
* else ignore the initialization.
*
* @param config, [NetworkXConfig] reference
*/
fun enable(config: NetworkXConfig) {
try {
if (manager != null) {
return
}
manager = NetworkXManager(application)
manager = NetworkXManager(config.application, config.enableSpeedMeter)
} catch (e: Exception) {
throw e
}
Expand Down
97 changes: 97 additions & 0 deletions NetworkX/src/main/java/com/rommansabbir/networkx/TrafficUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package com.rommansabbir.networkx

import android.net.TrafficStats
import kotlinx.coroutines.*
import java.util.*

/**
* To monitor current network speed.
* All the work should be done by using [CoroutineScope].
* So that all ongoing operations can be cancelled on demand.
*/
class TrafficUtils {
private var ioScope: CoroutineScope? = null
private var mainScope: CoroutineScope? = null

// Initialize all scopes
fun enabledScopes() {
synchronized(Any()) {
ioScope?.cancel()
ioScope = CoroutineScope(Dispatchers.IO)
mainScope?.cancel()
mainScope = CoroutineScope(Dispatchers.Main)
}
}

// Release all scopes
fun releaseScopes() {
synchronized(Any()) {
ioScope?.cancel()
ioScope = null
mainScope?.cancel()
mainScope = null
}
}

private val gb: Long = 1000000000
private val mb: Long = 1000000
private val kb: Long = 1000

/**
* Get current network speed which return an instance of [LastKnownSpeed] instance using the callback.
*
* @param onUpdate, callback for [LastKnownSpeed] update
*/
fun getNetworkSpeed(onUpdate: (lastKnownSpeed: LastKnownSpeed) -> Unit) {
try {
if (ioScope == null || mainScope == null) {
releaseScopes()
enabledScopes()
}
ioScope?.launch {
val downloadSpeedOutput: String
val units: String
val mBytesPrevious = TrafficStats.getTotalRxBytes() + TrafficStats.getTotalTxBytes()
delay(1000)
val mBytesCurrent = TrafficStats.getTotalRxBytes() + TrafficStats.getTotalTxBytes()
val mNetworkSpeed = mBytesCurrent - mBytesPrevious
val mDownloadSpeedWithDecimals: Float
val networkSpeedType: String
when {
mNetworkSpeed >= gb -> {
mDownloadSpeedWithDecimals = mNetworkSpeed.toFloat() / gb.toFloat()
units = " ${NetworkSpeedType.GB}"
networkSpeedType = NetworkSpeedType.GB
}
mNetworkSpeed >= mb -> {
mDownloadSpeedWithDecimals = mNetworkSpeed.toFloat() / mb.toFloat()
units = " ${NetworkSpeedType.MB}"
networkSpeedType = NetworkSpeedType.MB
}
else -> {
mDownloadSpeedWithDecimals = mNetworkSpeed.toFloat() / kb.toFloat()
units = " ${NetworkSpeedType.KB}"
networkSpeedType = NetworkSpeedType.KB
}
}
downloadSpeedOutput =
if (units != " ${NetworkSpeedType.KB}" && mDownloadSpeedWithDecimals < 100) {
String.format(Locale.US, "%.1f", mDownloadSpeedWithDecimals)
} else {
mDownloadSpeedWithDecimals.toInt().toString()
}
mainScope?.launch {
onUpdate.invoke(
LastKnownSpeed(
downloadSpeedOutput,
networkSpeedType,
downloadSpeedOutput + units
)
)
}
}
} catch (e: Exception) {
throw e
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
package com.rommansabbir.networkx.exceptions

@Deprecated("Deprecated. Use NetworkXProvider instead")
class NetworkXNotInitializedException :
Exception("Did you initialized NetworkXCore from onCreate() in your application class?") {
}
Exception("Did you initialized NetworkX with NetworkXConfig from onCreate() in your application class?")
Loading

0 comments on commit a89d54d

Please sign in to comment.