-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
471 additions
and
4 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
48 changes: 48 additions & 0 deletions
48
...id/src/main/java/com/amplitude/android/plugins/AndroidNetworkConnectivityCheckerPlugin.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,48 @@ | ||
package com.amplitude.android.plugins | ||
|
||
import com.amplitude.android.Configuration | ||
import com.amplitude.android.utilities.AndroidNetworkConnectivityChecker | ||
import com.amplitude.android.utilities.AndroidNetworkListener | ||
import com.amplitude.core.Amplitude | ||
import com.amplitude.core.platform.Plugin | ||
import kotlinx.coroutines.launch | ||
|
||
class AndroidNetworkConnectivityCheckerPlugin : Plugin { | ||
override val type: Plugin.Type = Plugin.Type.Before | ||
override lateinit var amplitude: Amplitude | ||
internal lateinit var networkConnectivityChecker: AndroidNetworkConnectivityChecker | ||
internal lateinit var networkListener: AndroidNetworkListener | ||
|
||
companion object { | ||
val Disabled = null | ||
} | ||
|
||
override fun setup(amplitude: Amplitude) { | ||
super.setup(amplitude) | ||
amplitude.logger.debug("Installing AndroidNetworkConnectivityPlugin, offline feature should be supported.") | ||
networkConnectivityChecker = AndroidNetworkConnectivityChecker((amplitude.configuration as Configuration).context, amplitude.logger) | ||
amplitude.amplitudeScope.launch(amplitude.storageIODispatcher) { | ||
amplitude.configuration.offline = !networkConnectivityChecker.isConnected() | ||
} | ||
val networkChangeHandler = | ||
object : AndroidNetworkListener.NetworkChangeCallback { | ||
override fun onNetworkAvailable() { | ||
amplitude.logger.debug("AndroidNetworkListener, onNetworkAvailable.") | ||
amplitude.configuration.offline = false | ||
amplitude.flush() | ||
} | ||
|
||
override fun onNetworkUnavailable() { | ||
amplitude.logger.debug("AndroidNetworkListener, onNetworkUnavailable.") | ||
amplitude.configuration.offline = true | ||
} | ||
} | ||
networkListener = AndroidNetworkListener((amplitude.configuration as Configuration).context) | ||
networkListener.setNetworkChangeCallback(networkChangeHandler) | ||
networkListener.startListening() | ||
} | ||
|
||
override fun teardown() { | ||
networkListener.stopListening() | ||
} | ||
} |
63 changes: 63 additions & 0 deletions
63
android/src/main/java/com/amplitude/android/utilities/AndroidNetworkConnectivityChecker.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,63 @@ | ||
package com.amplitude.android.utilities | ||
|
||
import android.annotation.SuppressLint | ||
import android.content.Context | ||
import android.content.pm.PackageManager | ||
import android.net.ConnectivityManager | ||
import android.net.NetworkCapabilities | ||
import android.os.Build | ||
import com.amplitude.common.Logger | ||
|
||
class AndroidNetworkConnectivityChecker(private val context: Context, private val logger: Logger) { | ||
companion object { | ||
private const val ACCESS_NETWORK_STATE = "android.permission.ACCESS_NETWORK_STATE" | ||
} | ||
|
||
private val hasPermission: Boolean | ||
internal var isMarshmallowAndAbove: Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M | ||
|
||
init { | ||
hasPermission = hasPermission(context, ACCESS_NETWORK_STATE) | ||
if (!hasPermission) { | ||
logger.warn( | ||
@Suppress("ktlint:standard:max-line-length") | ||
"No ACCESS_NETWORK_STATE permission, offline mode is not supported. To enable, add <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" /> to your AndroidManifest.xml. Learn more at https://www.docs.developers.amplitude.com/data/sdks/android-kotlin/#offline-mode", | ||
) | ||
} | ||
} | ||
|
||
@SuppressLint("MissingPermission", "NewApi") | ||
fun isConnected(): Boolean { | ||
// Assume connection and proceed. | ||
// Events will be treated like online | ||
// regardless network connectivity | ||
if (!hasPermission) { | ||
return true | ||
} | ||
|
||
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) | ||
if (cm is ConnectivityManager) { | ||
if (isMarshmallowAndAbove) { | ||
val network = cm.activeNetwork ?: return false | ||
val capabilities = cm.getNetworkCapabilities(network) ?: return false | ||
|
||
return capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) || | ||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) | ||
} else { | ||
@SuppressLint("MissingPermission") | ||
val networkInfo = cm.activeNetworkInfo | ||
return networkInfo != null && networkInfo.isConnectedOrConnecting | ||
} | ||
} else { | ||
logger.debug("Service is not an instance of ConnectivityManager. Offline mode is not supported") | ||
return true | ||
} | ||
} | ||
|
||
private fun hasPermission( | ||
context: Context, | ||
permission: String, | ||
): Boolean { | ||
return context.checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED | ||
} | ||
} |
102 changes: 102 additions & 0 deletions
102
android/src/main/java/com/amplitude/android/utilities/AndroidNetworkListener.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,102 @@ | ||
package com.amplitude.android.utilities | ||
|
||
import android.annotation.SuppressLint | ||
import android.content.BroadcastReceiver | ||
import android.content.Context | ||
import android.content.Intent | ||
import android.content.IntentFilter | ||
import android.net.ConnectivityManager | ||
import android.net.Network | ||
import android.net.NetworkCapabilities | ||
import android.net.NetworkRequest | ||
import android.os.Build | ||
import java.lang.IllegalArgumentException | ||
|
||
class AndroidNetworkListener(private val context: Context) { | ||
private var networkCallback: NetworkChangeCallback? = null | ||
private var networkCallbackForLowerApiLevels: BroadcastReceiver? = null | ||
private var networkCallbackForHigherApiLevels: ConnectivityManager.NetworkCallback? = null | ||
|
||
interface NetworkChangeCallback { | ||
fun onNetworkAvailable() | ||
|
||
fun onNetworkUnavailable() | ||
} | ||
|
||
fun setNetworkChangeCallback(callback: NetworkChangeCallback) { | ||
this.networkCallback = callback | ||
} | ||
|
||
fun startListening() { | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { | ||
setupNetworkCallback() | ||
} else { | ||
setupBroadcastReceiver() | ||
} | ||
} | ||
|
||
@SuppressLint("NewApi", "MissingPermission") | ||
// startListening() checks API level | ||
// ACCESS_NETWORK_STATE permission should be added manually by users to enable this feature | ||
private fun setupNetworkCallback() { | ||
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager | ||
val networkRequest = | ||
NetworkRequest.Builder() | ||
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) | ||
.build() | ||
|
||
networkCallbackForHigherApiLevels = | ||
object : ConnectivityManager.NetworkCallback() { | ||
override fun onAvailable(network: Network) { | ||
networkCallback?.onNetworkAvailable() | ||
} | ||
|
||
override fun onLost(network: Network) { | ||
networkCallback?.onNetworkUnavailable() | ||
} | ||
} | ||
|
||
connectivityManager.registerNetworkCallback(networkRequest, networkCallbackForHigherApiLevels!!) | ||
} | ||
|
||
private fun setupBroadcastReceiver() { | ||
networkCallbackForLowerApiLevels = | ||
object : BroadcastReceiver() { | ||
@SuppressLint("MissingPermission") | ||
override fun onReceive( | ||
context: Context, | ||
intent: Intent, | ||
) { | ||
if (ConnectivityManager.CONNECTIVITY_ACTION == intent.action) { | ||
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager | ||
val activeNetwork = connectivityManager.activeNetworkInfo | ||
val isConnected = activeNetwork?.isConnectedOrConnecting == true | ||
|
||
if (isConnected) { | ||
networkCallback?.onNetworkAvailable() | ||
} else { | ||
networkCallback?.onNetworkUnavailable() | ||
} | ||
} | ||
} | ||
} | ||
|
||
val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION) | ||
context.registerReceiver(networkCallbackForLowerApiLevels, filter) | ||
} | ||
|
||
fun stopListening() { | ||
try { | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { | ||
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager | ||
networkCallbackForHigherApiLevels?.let { connectivityManager.unregisterNetworkCallback(it) } | ||
} else { | ||
networkCallbackForLowerApiLevels?.let { context.unregisterReceiver(it) } | ||
} | ||
} catch (e: IllegalArgumentException) { | ||
// callback was already unregistered. | ||
} catch (e: IllegalStateException) { | ||
// shutdown process is in progress and certain operations are not allowed. | ||
} | ||
} | ||
} |
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
Oops, something went wrong.