From 4d7d3f1ec73946ee088f0218164f7514a6620d56 Mon Sep 17 00:00:00 2001 From: Miguel Beltran Date: Thu, 11 Apr 2024 07:39:23 +0200 Subject: [PATCH 01/11] fix: Ensure Connectivity on Android is correctly reported when lost --- .../plus/connectivity/Connectivity.java | 188 ++++++++++-------- .../ConnectivityBroadcastReceiver.java | 136 +++++++------ 2 files changed, 174 insertions(+), 150 deletions(-) diff --git a/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/Connectivity.java b/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/Connectivity.java index 2e54d37758..71b0ec99c0 100644 --- a/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/Connectivity.java +++ b/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/Connectivity.java @@ -8,102 +8,118 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.os.Build; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + import java.util.ArrayList; import java.util.List; -/** Reports connectivity related information such as connectivity type and wifi information. */ +/** + * Reports connectivity related information such as connectivity type and wifi information. + */ public class Connectivity { - static final String CONNECTIVITY_NONE = "none"; - static final String CONNECTIVITY_WIFI = "wifi"; - static final String CONNECTIVITY_MOBILE = "mobile"; - static final String CONNECTIVITY_ETHERNET = "ethernet"; - static final String CONNECTIVITY_BLUETOOTH = "bluetooth"; - static final String CONNECTIVITY_VPN = "vpn"; - static final String CONNECTIVITY_OTHER = "other"; - private final ConnectivityManager connectivityManager; + static final String CONNECTIVITY_NONE = "none"; + static final String CONNECTIVITY_WIFI = "wifi"; + static final String CONNECTIVITY_MOBILE = "mobile"; + static final String CONNECTIVITY_ETHERNET = "ethernet"; + static final String CONNECTIVITY_BLUETOOTH = "bluetooth"; + static final String CONNECTIVITY_VPN = "vpn"; + static final String CONNECTIVITY_OTHER = "other"; + private final ConnectivityManager connectivityManager; - public Connectivity(ConnectivityManager connectivityManager) { - this.connectivityManager = connectivityManager; - } + public Connectivity(ConnectivityManager connectivityManager) { + this.connectivityManager = connectivityManager; + } - List getNetworkTypes() { - List types = new ArrayList<>(); - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - Network network = connectivityManager.getActiveNetwork(); - NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network); - if (capabilities == null - || !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { - types.add(CONNECTIVITY_NONE); - return types; - } - if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) - || capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI_AWARE)) { - types.add(CONNECTIVITY_WIFI); - } - if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) { - types.add(CONNECTIVITY_ETHERNET); - } - if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) { - types.add(CONNECTIVITY_VPN); - } - if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { - types.add(CONNECTIVITY_MOBILE); - } - if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH)) { - types.add(CONNECTIVITY_BLUETOOTH); - } - if (types.isEmpty() - && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { - types.add(CONNECTIVITY_OTHER); - } - if (types.isEmpty()) { - types.add(CONNECTIVITY_NONE); - } - } else { - // For legacy versions, return a single type as before or adapt similarly if multiple types - // need to be supported - return getNetworkTypesLegacy(); + List getNetworkTypes() { + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + Network network = connectivityManager.getActiveNetwork(); + return getCapabilitiesFromNetwork(network); + } else { + // For legacy versions, return a single type as before or adapt similarly if multiple types + // need to be supported + return getNetworkTypesLegacy(); + } } - return types; - } + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + List getCapabilitiesFromNetwork(Network network) { + NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network); + return getCapabilitiesList(capabilities); + } - @SuppressWarnings("deprecation") - private List getNetworkTypesLegacy() { - // handle type for Android versions less than Android 6 - android.net.NetworkInfo info = connectivityManager.getActiveNetworkInfo(); - List types = new ArrayList<>(); - if (info == null || !info.isConnected()) { - types.add(CONNECTIVITY_NONE); - return types; + @NonNull + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + List getCapabilitiesList(NetworkCapabilities capabilities) { + List types = new ArrayList<>(); + if (capabilities == null + || !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { + types.add(CONNECTIVITY_NONE); + return types; + } + if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) + || capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI_AWARE)) { + types.add(CONNECTIVITY_WIFI); + } + if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) { + types.add(CONNECTIVITY_ETHERNET); + } + if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) { + types.add(CONNECTIVITY_VPN); + } + if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + types.add(CONNECTIVITY_MOBILE); + } + if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH)) { + types.add(CONNECTIVITY_BLUETOOTH); + } + if (types.isEmpty() + && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { + types.add(CONNECTIVITY_OTHER); + } + if (types.isEmpty()) { + types.add(CONNECTIVITY_NONE); + } + return types; } - int type = info.getType(); - switch (type) { - case ConnectivityManager.TYPE_BLUETOOTH: - types.add(CONNECTIVITY_BLUETOOTH); - break; - case ConnectivityManager.TYPE_ETHERNET: - types.add(CONNECTIVITY_ETHERNET); - break; - case ConnectivityManager.TYPE_WIFI: - case ConnectivityManager.TYPE_WIMAX: - types.add(CONNECTIVITY_WIFI); - break; - case ConnectivityManager.TYPE_VPN: - types.add(CONNECTIVITY_VPN); - break; - case ConnectivityManager.TYPE_MOBILE: - case ConnectivityManager.TYPE_MOBILE_DUN: - case ConnectivityManager.TYPE_MOBILE_HIPRI: - types.add(CONNECTIVITY_MOBILE); - break; - default: - types.add(CONNECTIVITY_OTHER); + + @SuppressWarnings("deprecation") + private List getNetworkTypesLegacy() { + // handle type for Android versions less than Android 6 + android.net.NetworkInfo info = connectivityManager.getActiveNetworkInfo(); + List types = new ArrayList<>(); + if (info == null || !info.isConnected()) { + types.add(CONNECTIVITY_NONE); + return types; + } + int type = info.getType(); + switch (type) { + case ConnectivityManager.TYPE_BLUETOOTH: + types.add(CONNECTIVITY_BLUETOOTH); + break; + case ConnectivityManager.TYPE_ETHERNET: + types.add(CONNECTIVITY_ETHERNET); + break; + case ConnectivityManager.TYPE_WIFI: + case ConnectivityManager.TYPE_WIMAX: + types.add(CONNECTIVITY_WIFI); + break; + case ConnectivityManager.TYPE_VPN: + types.add(CONNECTIVITY_VPN); + break; + case ConnectivityManager.TYPE_MOBILE: + case ConnectivityManager.TYPE_MOBILE_DUN: + case ConnectivityManager.TYPE_MOBILE_HIPRI: + types.add(CONNECTIVITY_MOBILE); + break; + default: + types.add(CONNECTIVITY_OTHER); + } + return types; } - return types; - } - public ConnectivityManager getConnectivityManager() { - return connectivityManager; - } + public ConnectivityManager getConnectivityManager() { + return connectivityManager; + } } diff --git a/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java b/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java index 07a01796c1..a094790e2c 100644 --- a/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java +++ b/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java @@ -4,6 +4,8 @@ package dev.fluttercommunity.plus.connectivity; +import static dev.fluttercommunity.plus.connectivity.Connectivity.CONNECTIVITY_NONE; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -14,6 +16,9 @@ import android.os.Build; import android.os.Handler; import android.os.Looper; + +import java.util.List; + import io.flutter.plugin.common.EventChannel; /** @@ -25,78 +30,81 @@ * to set up the receiver. */ public class ConnectivityBroadcastReceiver extends BroadcastReceiver - implements EventChannel.StreamHandler { - private final Context context; - private final Connectivity connectivity; - private EventChannel.EventSink events; - private final Handler mainHandler = new Handler(Looper.getMainLooper()); - private ConnectivityManager.NetworkCallback networkCallback; - public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; + implements EventChannel.StreamHandler { + private final Context context; + private final Connectivity connectivity; + private EventChannel.EventSink events; + private final Handler mainHandler = new Handler(Looper.getMainLooper()); + private ConnectivityManager.NetworkCallback networkCallback; + public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; - public ConnectivityBroadcastReceiver(Context context, Connectivity connectivity) { - this.context = context; - this.connectivity = connectivity; - } + public ConnectivityBroadcastReceiver(Context context, Connectivity connectivity) { + this.context = context; + this.connectivity = connectivity; + } - @Override - public void onListen(Object arguments, EventChannel.EventSink events) { - this.events = events; - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - networkCallback = - new ConnectivityManager.NetworkCallback() { - @Override - public void onAvailable(Network network) { - sendEvent(); - } + @Override + public void onListen(Object arguments, EventChannel.EventSink events) { + this.events = events; + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + networkCallback = + new ConnectivityManager.NetworkCallback() { + @Override + public void onAvailable(Network network) { + // Use the provided Network object in the callback + sendEvent(connectivity.getCapabilitiesFromNetwork(network)); + } - @Override - public void onCapabilitiesChanged( - Network network, NetworkCapabilities networkCapabilities) { - sendEvent(); - } + @Override + public void onCapabilitiesChanged( + Network network, NetworkCapabilities networkCapabilities) { + // Use the provided NetworkCapabilities in the callback + sendEvent(connectivity.getCapabilitiesList(networkCapabilities)); + } - @Override - public void onLost(Network network) { - sendEvent(); - } - }; - connectivity.getConnectivityManager().registerDefaultNetworkCallback(networkCallback); - } else { - context.registerReceiver(this, new IntentFilter(CONNECTIVITY_ACTION)); + @Override + public void onLost(Network network) { + // Send NONE to ensure no network is reported when connectivity is lost + sendEvent(List.of(CONNECTIVITY_NONE)); + } + }; + connectivity.getConnectivityManager().registerDefaultNetworkCallback(networkCallback); + } else { + context.registerReceiver(this, new IntentFilter(CONNECTIVITY_ACTION)); + } + // Need to emit first event with connectivity types without waiting for first change in system + // that might happen much later + sendEvent(connectivity.getNetworkTypes()); } - // Need to emit first event with connectivity types without waiting for first change in system - // that might happen much later - sendEvent(); - } - @Override - public void onCancel(Object arguments) { - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - if (networkCallback != null) { - connectivity.getConnectivityManager().unregisterNetworkCallback(networkCallback); - networkCallback = null; - } - } else { - try { - context.unregisterReceiver(this); - } catch (Exception e) { - // listen never called, ignore the error - } + @Override + public void onCancel(Object arguments) { + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (networkCallback != null) { + connectivity.getConnectivityManager().unregisterNetworkCallback(networkCallback); + networkCallback = null; + } + } else { + try { + context.unregisterReceiver(this); + } catch (Exception e) { + // listen never called, ignore the error + } + } } - } - @Override - public void onReceive(Context context, Intent intent) { - if (events != null) { - events.success(connectivity.getNetworkTypes()); + @Override + public void onReceive(Context context, Intent intent) { + if (events != null) { + events.success(connectivity.getNetworkTypes()); + } } - } - private void sendEvent() { - Runnable runnable = () -> events.success(connectivity.getNetworkTypes()); - // The dalay is needed because callback methods suffer from race conditions. - // More info: - // https://developer.android.com/develop/connectivity/network-ops/reading-network-state#listening-events - mainHandler.postDelayed(runnable, 100); - } + private void sendEvent(List networkTypes) { + Runnable runnable = () -> events.success(networkTypes); + // The dalay is needed because callback methods suffer from race conditions. + // More info: + // https://developer.android.com/develop/connectivity/network-ops/reading-network-state#listening-events + mainHandler.postDelayed(runnable, 100); + } } From cfc123acc5096a9863ebc55a1ea28effd3c2ec96 Mon Sep 17 00:00:00 2001 From: Miguel Beltran Date: Thu, 11 Apr 2024 07:43:24 +0200 Subject: [PATCH 02/11] code format --- .../plus/connectivity/Connectivity.java | 194 +++++++++--------- .../ConnectivityBroadcastReceiver.java | 138 ++++++------- .../share_plus/share_plus/windows/vector.h | 4 +- 3 files changed, 165 insertions(+), 171 deletions(-) diff --git a/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/Connectivity.java b/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/Connectivity.java index 71b0ec99c0..d5e5cbbde2 100644 --- a/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/Connectivity.java +++ b/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/Connectivity.java @@ -8,118 +8,114 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.os.Build; - import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; - import java.util.ArrayList; import java.util.List; -/** - * Reports connectivity related information such as connectivity type and wifi information. - */ +/** Reports connectivity related information such as connectivity type and wifi information. */ public class Connectivity { - static final String CONNECTIVITY_NONE = "none"; - static final String CONNECTIVITY_WIFI = "wifi"; - static final String CONNECTIVITY_MOBILE = "mobile"; - static final String CONNECTIVITY_ETHERNET = "ethernet"; - static final String CONNECTIVITY_BLUETOOTH = "bluetooth"; - static final String CONNECTIVITY_VPN = "vpn"; - static final String CONNECTIVITY_OTHER = "other"; - private final ConnectivityManager connectivityManager; + static final String CONNECTIVITY_NONE = "none"; + static final String CONNECTIVITY_WIFI = "wifi"; + static final String CONNECTIVITY_MOBILE = "mobile"; + static final String CONNECTIVITY_ETHERNET = "ethernet"; + static final String CONNECTIVITY_BLUETOOTH = "bluetooth"; + static final String CONNECTIVITY_VPN = "vpn"; + static final String CONNECTIVITY_OTHER = "other"; + private final ConnectivityManager connectivityManager; - public Connectivity(ConnectivityManager connectivityManager) { - this.connectivityManager = connectivityManager; - } + public Connectivity(ConnectivityManager connectivityManager) { + this.connectivityManager = connectivityManager; + } - List getNetworkTypes() { - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - Network network = connectivityManager.getActiveNetwork(); - return getCapabilitiesFromNetwork(network); - } else { - // For legacy versions, return a single type as before or adapt similarly if multiple types - // need to be supported - return getNetworkTypesLegacy(); - } + List getNetworkTypes() { + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + Network network = connectivityManager.getActiveNetwork(); + return getCapabilitiesFromNetwork(network); + } else { + // For legacy versions, return a single type as before or adapt similarly if multiple types + // need to be supported + return getNetworkTypesLegacy(); } + } - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - List getCapabilitiesFromNetwork(Network network) { - NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network); - return getCapabilitiesList(capabilities); - } + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + List getCapabilitiesFromNetwork(Network network) { + NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network); + return getCapabilitiesList(capabilities); + } - @NonNull - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - List getCapabilitiesList(NetworkCapabilities capabilities) { - List types = new ArrayList<>(); - if (capabilities == null - || !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { - types.add(CONNECTIVITY_NONE); - return types; - } - if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) - || capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI_AWARE)) { - types.add(CONNECTIVITY_WIFI); - } - if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) { - types.add(CONNECTIVITY_ETHERNET); - } - if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) { - types.add(CONNECTIVITY_VPN); - } - if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { - types.add(CONNECTIVITY_MOBILE); - } - if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH)) { - types.add(CONNECTIVITY_BLUETOOTH); - } - if (types.isEmpty() - && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { - types.add(CONNECTIVITY_OTHER); - } - if (types.isEmpty()) { - types.add(CONNECTIVITY_NONE); - } - return types; + @NonNull + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + List getCapabilitiesList(NetworkCapabilities capabilities) { + List types = new ArrayList<>(); + if (capabilities == null + || !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { + types.add(CONNECTIVITY_NONE); + return types; } - - @SuppressWarnings("deprecation") - private List getNetworkTypesLegacy() { - // handle type for Android versions less than Android 6 - android.net.NetworkInfo info = connectivityManager.getActiveNetworkInfo(); - List types = new ArrayList<>(); - if (info == null || !info.isConnected()) { - types.add(CONNECTIVITY_NONE); - return types; - } - int type = info.getType(); - switch (type) { - case ConnectivityManager.TYPE_BLUETOOTH: - types.add(CONNECTIVITY_BLUETOOTH); - break; - case ConnectivityManager.TYPE_ETHERNET: - types.add(CONNECTIVITY_ETHERNET); - break; - case ConnectivityManager.TYPE_WIFI: - case ConnectivityManager.TYPE_WIMAX: - types.add(CONNECTIVITY_WIFI); - break; - case ConnectivityManager.TYPE_VPN: - types.add(CONNECTIVITY_VPN); - break; - case ConnectivityManager.TYPE_MOBILE: - case ConnectivityManager.TYPE_MOBILE_DUN: - case ConnectivityManager.TYPE_MOBILE_HIPRI: - types.add(CONNECTIVITY_MOBILE); - break; - default: - types.add(CONNECTIVITY_OTHER); - } - return types; + if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) + || capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI_AWARE)) { + types.add(CONNECTIVITY_WIFI); + } + if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) { + types.add(CONNECTIVITY_ETHERNET); + } + if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) { + types.add(CONNECTIVITY_VPN); } + if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + types.add(CONNECTIVITY_MOBILE); + } + if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH)) { + types.add(CONNECTIVITY_BLUETOOTH); + } + if (types.isEmpty() + && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { + types.add(CONNECTIVITY_OTHER); + } + if (types.isEmpty()) { + types.add(CONNECTIVITY_NONE); + } + return types; + } - public ConnectivityManager getConnectivityManager() { - return connectivityManager; + @SuppressWarnings("deprecation") + private List getNetworkTypesLegacy() { + // handle type for Android versions less than Android 6 + android.net.NetworkInfo info = connectivityManager.getActiveNetworkInfo(); + List types = new ArrayList<>(); + if (info == null || !info.isConnected()) { + types.add(CONNECTIVITY_NONE); + return types; + } + int type = info.getType(); + switch (type) { + case ConnectivityManager.TYPE_BLUETOOTH: + types.add(CONNECTIVITY_BLUETOOTH); + break; + case ConnectivityManager.TYPE_ETHERNET: + types.add(CONNECTIVITY_ETHERNET); + break; + case ConnectivityManager.TYPE_WIFI: + case ConnectivityManager.TYPE_WIMAX: + types.add(CONNECTIVITY_WIFI); + break; + case ConnectivityManager.TYPE_VPN: + types.add(CONNECTIVITY_VPN); + break; + case ConnectivityManager.TYPE_MOBILE: + case ConnectivityManager.TYPE_MOBILE_DUN: + case ConnectivityManager.TYPE_MOBILE_HIPRI: + types.add(CONNECTIVITY_MOBILE); + break; + default: + types.add(CONNECTIVITY_OTHER); } + return types; + } + + public ConnectivityManager getConnectivityManager() { + return connectivityManager; + } } diff --git a/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java b/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java index a094790e2c..9f0fb65215 100644 --- a/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java +++ b/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java @@ -16,10 +16,8 @@ import android.os.Build; import android.os.Handler; import android.os.Looper; - -import java.util.List; - import io.flutter.plugin.common.EventChannel; +import java.util.List; /** * The ConnectivityBroadcastReceiver receives the connectivity updates and send them to the UIThread @@ -30,81 +28,81 @@ * to set up the receiver. */ public class ConnectivityBroadcastReceiver extends BroadcastReceiver - implements EventChannel.StreamHandler { - private final Context context; - private final Connectivity connectivity; - private EventChannel.EventSink events; - private final Handler mainHandler = new Handler(Looper.getMainLooper()); - private ConnectivityManager.NetworkCallback networkCallback; - public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; - - public ConnectivityBroadcastReceiver(Context context, Connectivity connectivity) { - this.context = context; - this.connectivity = connectivity; - } + implements EventChannel.StreamHandler { + private final Context context; + private final Connectivity connectivity; + private EventChannel.EventSink events; + private final Handler mainHandler = new Handler(Looper.getMainLooper()); + private ConnectivityManager.NetworkCallback networkCallback; + public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; - @Override - public void onListen(Object arguments, EventChannel.EventSink events) { - this.events = events; - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - networkCallback = - new ConnectivityManager.NetworkCallback() { - @Override - public void onAvailable(Network network) { - // Use the provided Network object in the callback - sendEvent(connectivity.getCapabilitiesFromNetwork(network)); - } + public ConnectivityBroadcastReceiver(Context context, Connectivity connectivity) { + this.context = context; + this.connectivity = connectivity; + } - @Override - public void onCapabilitiesChanged( - Network network, NetworkCapabilities networkCapabilities) { - // Use the provided NetworkCapabilities in the callback - sendEvent(connectivity.getCapabilitiesList(networkCapabilities)); - } - - @Override - public void onLost(Network network) { - // Send NONE to ensure no network is reported when connectivity is lost - sendEvent(List.of(CONNECTIVITY_NONE)); - } - }; - connectivity.getConnectivityManager().registerDefaultNetworkCallback(networkCallback); - } else { - context.registerReceiver(this, new IntentFilter(CONNECTIVITY_ACTION)); - } - // Need to emit first event with connectivity types without waiting for first change in system - // that might happen much later - sendEvent(connectivity.getNetworkTypes()); - } + @Override + public void onListen(Object arguments, EventChannel.EventSink events) { + this.events = events; + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + networkCallback = + new ConnectivityManager.NetworkCallback() { + @Override + public void onAvailable(Network network) { + // Use the provided Network object in the callback + sendEvent(connectivity.getCapabilitiesFromNetwork(network)); + } - @Override - public void onCancel(Object arguments) { - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - if (networkCallback != null) { - connectivity.getConnectivityManager().unregisterNetworkCallback(networkCallback); - networkCallback = null; + @Override + public void onCapabilitiesChanged( + Network network, NetworkCapabilities networkCapabilities) { + // Use the provided NetworkCapabilities in the callback + sendEvent(connectivity.getCapabilitiesList(networkCapabilities)); } - } else { - try { - context.unregisterReceiver(this); - } catch (Exception e) { - // listen never called, ignore the error + + @Override + public void onLost(Network network) { + // Send NONE to ensure no network is reported when connectivity is lost + sendEvent(List.of(CONNECTIVITY_NONE)); } - } + }; + connectivity.getConnectivityManager().registerDefaultNetworkCallback(networkCallback); + } else { + context.registerReceiver(this, new IntentFilter(CONNECTIVITY_ACTION)); } + // Need to emit first event with connectivity types without waiting for first change in system + // that might happen much later + sendEvent(connectivity.getNetworkTypes()); + } - @Override - public void onReceive(Context context, Intent intent) { - if (events != null) { - events.success(connectivity.getNetworkTypes()); - } + @Override + public void onCancel(Object arguments) { + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (networkCallback != null) { + connectivity.getConnectivityManager().unregisterNetworkCallback(networkCallback); + networkCallback = null; + } + } else { + try { + context.unregisterReceiver(this); + } catch (Exception e) { + // listen never called, ignore the error + } } + } - private void sendEvent(List networkTypes) { - Runnable runnable = () -> events.success(networkTypes); - // The dalay is needed because callback methods suffer from race conditions. - // More info: - // https://developer.android.com/develop/connectivity/network-ops/reading-network-state#listening-events - mainHandler.postDelayed(runnable, 100); + @Override + public void onReceive(Context context, Intent intent) { + if (events != null) { + events.success(connectivity.getNetworkTypes()); } + } + + private void sendEvent(List networkTypes) { + Runnable runnable = () -> events.success(networkTypes); + // The dalay is needed because callback methods suffer from race conditions. + // More info: + // https://developer.android.com/develop/connectivity/network-ops/reading-network-state#listening-events + mainHandler.postDelayed(runnable, 100); + } } diff --git a/packages/share_plus/share_plus/windows/vector.h b/packages/share_plus/share_plus/windows/vector.h index 105e42c222..8bf96d80ec 100644 --- a/packages/share_plus/share_plus/windows/vector.h +++ b/packages/share_plus/share_plus/windows/vector.h @@ -65,8 +65,8 @@ __declspec(noreturn) __declspec(noinline) inline void ThrowHR(HRESULT hr, ThrowHR(hr); } -__declspec(noreturn) __declspec(noinline) inline void ThrowHR( - HRESULT hr, wchar_t const *message) { +__declspec(noreturn) __declspec(noinline) inline void +ThrowHR(HRESULT hr, wchar_t const *message) { using ::Microsoft::WRL::Wrappers::HStringReference; ThrowHR(hr, HStringReference(message).Get()); From 16129fcb0c12d647ab1bf5b4ff9dca8b8f1993a2 Mon Sep 17 00:00:00 2001 From: Miguel Beltran Date: Thu, 11 Apr 2024 07:44:07 +0200 Subject: [PATCH 03/11] revert change --- packages/share_plus/share_plus/windows/vector.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/share_plus/share_plus/windows/vector.h b/packages/share_plus/share_plus/windows/vector.h index 8bf96d80ec..105e42c222 100644 --- a/packages/share_plus/share_plus/windows/vector.h +++ b/packages/share_plus/share_plus/windows/vector.h @@ -65,8 +65,8 @@ __declspec(noreturn) __declspec(noinline) inline void ThrowHR(HRESULT hr, ThrowHR(hr); } -__declspec(noreturn) __declspec(noinline) inline void -ThrowHR(HRESULT hr, wchar_t const *message) { +__declspec(noreturn) __declspec(noinline) inline void ThrowHR( + HRESULT hr, wchar_t const *message) { using ::Microsoft::WRL::Wrappers::HStringReference; ThrowHR(hr, HStringReference(message).Get()); From b50f835a797a7197b29d7104ac2319201ed5f5f1 Mon Sep 17 00:00:00 2001 From: Miguel Beltran Date: Thu, 11 Apr 2024 13:42:18 +0200 Subject: [PATCH 04/11] add docs --- .../ConnectivityBroadcastReceiver.java | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java b/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java index 9f0fb65215..1b1a719fca 100644 --- a/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java +++ b/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java @@ -49,20 +49,35 @@ public void onListen(Object arguments, EventChannel.EventSink events) { new ConnectivityManager.NetworkCallback() { @Override public void onAvailable(Network network) { - // Use the provided Network object in the callback + // onAvailable is called when the phone switches to a new network + // e.g. the phone was offline and gets wifi connection + // or the phone was on wifi and now switches to mobile. + // The plugin sends the current capability connection to the users. sendEvent(connectivity.getCapabilitiesFromNetwork(network)); } @Override public void onCapabilitiesChanged( Network network, NetworkCapabilities networkCapabilities) { - // Use the provided NetworkCapabilities in the callback + // This callback is called multiple times after a call to onAvailable + // this also causes multiple callbacks to the Flutter layer. sendEvent(connectivity.getCapabilitiesList(networkCapabilities)); } @Override public void onLost(Network network) { - // Send NONE to ensure no network is reported when connectivity is lost + // This callback is called when a capability is lost. + + // e.g. user disconnected from wifi or disabled mobile data. + // If a user switches from wifi to mobile, + // onLost is called (for wifi), + + // then onAvailable is called afterwards (for mobile). + // Meaning the user receives [wifi] -> [none] -> [mobile] + + // If no other connectivity is present, onAvailable will not be called, + // and onLost is the only event that will happen. + // i.e. the user will only receive [wifi] -> [none] sendEvent(List.of(CONNECTIVITY_NONE)); } }; From f89f3a5f49c492cf4e12c69928c91c310a91ed84 Mon Sep 17 00:00:00 2001 From: Miguel Beltran Date: Thu, 11 Apr 2024 13:54:24 +0200 Subject: [PATCH 05/11] ensure distinct on connectivity changes --- .../connectivity_plus/example/lib/main.dart | 3 +++ .../connectivity_plus/lib/connectivity_plus.dart | 14 ++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/connectivity_plus/connectivity_plus/example/lib/main.dart b/packages/connectivity_plus/connectivity_plus/example/lib/main.dart index d13b94ef69..4904a6f6d4 100644 --- a/packages/connectivity_plus/connectivity_plus/example/lib/main.dart +++ b/packages/connectivity_plus/connectivity_plus/example/lib/main.dart @@ -8,6 +8,7 @@ import 'dart:async'; import 'dart:developer' as developer; import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -86,6 +87,8 @@ class _MyHomePageState extends State { setState(() { _connectionStatus = result; }); + // ignore: avoid_print + print('Connectivity changed: $_connectionStatus'); } @override diff --git a/packages/connectivity_plus/connectivity_plus/lib/connectivity_plus.dart b/packages/connectivity_plus/connectivity_plus/lib/connectivity_plus.dart index c514a81335..eb3223ed82 100644 --- a/packages/connectivity_plus/connectivity_plus/lib/connectivity_plus.dart +++ b/packages/connectivity_plus/connectivity_plus/lib/connectivity_plus.dart @@ -6,6 +6,8 @@ import 'dart:async'; import 'package:connectivity_plus_platform_interface/connectivity_plus_platform_interface.dart'; +import 'package:collection/collection.dart'; + // Export enums from the platform_interface so plugin users can use them directly. export 'package:connectivity_plus_platform_interface/connectivity_plus_platform_interface.dart' show ConnectivityResult; @@ -40,16 +42,20 @@ class Connectivity { /// status changes, this is a known issue that only affects simulators. /// For details see https://github.com/fluttercommunity/plus_plugins/issues/479. /// - /// On Android, the Stream may emit new values even when - /// the [ConnectivityResult] list remains the same. + /// On Android, the Stream may emit [ConnectivityResult.none] after + /// losing connection, then emit another [ConnectivityResult] for the + /// new network. + /// e.g. going from wifi to mobile would emit: + /// wifi -> none -> mobile /// /// The emitted list is never empty. In case of no connectivity, the list contains /// a single element of [ConnectivityResult.none]. Note also that this is the only /// case where [ConnectivityResult.none] is present. /// - /// This method doesn't filter events, nor it ensures distinct values. + /// This method applies [Stream.distinct] over the received events to ensure + /// only emiting when connectivity changes. Stream> get onConnectivityChanged { - return _platform.onConnectivityChanged; + return _platform.onConnectivityChanged.distinct((a, b) => a.equals(b)); } /// Checks the connection status of the device. From 2189dfb05a77787cec611156e223daa0c9796300 Mon Sep 17 00:00:00 2001 From: Miguel Beltran Date: Thu, 11 Apr 2024 13:56:23 +0200 Subject: [PATCH 06/11] lint fixes --- .../connectivity_plus/connectivity_plus/example/lib/main.dart | 1 - .../connectivity_plus/lib/connectivity_plus.dart | 1 - packages/connectivity_plus/connectivity_plus/pubspec.yaml | 1 + 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/connectivity_plus/connectivity_plus/example/lib/main.dart b/packages/connectivity_plus/connectivity_plus/example/lib/main.dart index 4904a6f6d4..29927da5dc 100644 --- a/packages/connectivity_plus/connectivity_plus/example/lib/main.dart +++ b/packages/connectivity_plus/connectivity_plus/example/lib/main.dart @@ -8,7 +8,6 @@ import 'dart:async'; import 'dart:developer' as developer; import 'package:connectivity_plus/connectivity_plus.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; diff --git a/packages/connectivity_plus/connectivity_plus/lib/connectivity_plus.dart b/packages/connectivity_plus/connectivity_plus/lib/connectivity_plus.dart index eb3223ed82..8d6a277e11 100644 --- a/packages/connectivity_plus/connectivity_plus/lib/connectivity_plus.dart +++ b/packages/connectivity_plus/connectivity_plus/lib/connectivity_plus.dart @@ -5,7 +5,6 @@ import 'dart:async'; import 'package:connectivity_plus_platform_interface/connectivity_plus_platform_interface.dart'; - import 'package:collection/collection.dart'; // Export enums from the platform_interface so plugin users can use them directly. diff --git a/packages/connectivity_plus/connectivity_plus/pubspec.yaml b/packages/connectivity_plus/connectivity_plus/pubspec.yaml index dfb5c841b4..64b280822a 100644 --- a/packages/connectivity_plus/connectivity_plus/pubspec.yaml +++ b/packages/connectivity_plus/connectivity_plus/pubspec.yaml @@ -41,6 +41,7 @@ dependencies: web: '>=0.3.0 <=0.6.0' meta: ^1.8.0 nm: ^0.5.0 + collection: ^1.18.0 dev_dependencies: flutter_test: From 68afaba84499b9284e48685485058500dd03ae80 Mon Sep 17 00:00:00 2001 From: Miguel Beltran Date: Thu, 11 Apr 2024 14:00:06 +0200 Subject: [PATCH 07/11] remove unnecessary delay --- .../plus/connectivity/ConnectivityBroadcastReceiver.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java b/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java index 1b1a719fca..637ad73ffe 100644 --- a/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java +++ b/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java @@ -115,9 +115,7 @@ public void onReceive(Context context, Intent intent) { private void sendEvent(List networkTypes) { Runnable runnable = () -> events.success(networkTypes); - // The dalay is needed because callback methods suffer from race conditions. - // More info: - // https://developer.android.com/develop/connectivity/network-ops/reading-network-state#listening-events - mainHandler.postDelayed(runnable, 100); + // Emit events on main thread + mainHandler.post(runnable); } } From 9c9d95fe8ce35372383a1dbfa2cb080fd9fa81b7 Mon Sep 17 00:00:00 2001 From: Miguel Beltran Date: Thu, 11 Apr 2024 14:12:16 +0200 Subject: [PATCH 08/11] update readme --- packages/connectivity_plus/connectivity_plus/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/connectivity_plus/connectivity_plus/README.md b/packages/connectivity_plus/connectivity_plus/README.md index acc674411f..3537fb754a 100644 --- a/packages/connectivity_plus/connectivity_plus/README.md +++ b/packages/connectivity_plus/connectivity_plus/README.md @@ -58,7 +58,9 @@ if (connectivityResult.contains(ConnectivityResult.mobile)) { ``` You can also listen for active connectivity types changes by subscribing to the stream -exposed by the plugin: +exposed by the plugin. + +This method should ensure emitting only distinct values. ```dart import 'package:connectivity_plus/connectivity_plus.dart'; @@ -99,6 +101,8 @@ _`none` is supported on all platforms by default._ Connectivity changes are no longer communicated to Android apps in the background starting with Android O (8.0). You should always check for connectivity status when your app is resumed. The broadcast is only useful when your application is in the foreground. +To ensure connectivity changes on Android are tracked correctly, an event containing `[ConnectivityResult.none]` may be emitted in during connectivity changes. e.g. Going from WiFi to mobile network may emit: `[wifi] -> [none] -> [mobile]`. + ### iOS & MacOS On iOS simulators, the connectivity types stream might not update when Wi-Fi status changes. This is a known issue. From 0948acfa4f11c8be2581ef01412c071286152392 Mon Sep 17 00:00:00 2001 From: Miguel Beltran Date: Wed, 17 Apr 2024 08:55:10 +0200 Subject: [PATCH 09/11] onLost sends event after delay --- .../ConnectivityBroadcastReceiver.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java b/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java index 637ad73ffe..324d3d234d 100644 --- a/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java +++ b/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java @@ -4,8 +4,6 @@ package dev.fluttercommunity.plus.connectivity; -import static dev.fluttercommunity.plus.connectivity.Connectivity.CONNECTIVITY_NONE; - import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -67,18 +65,13 @@ public void onCapabilitiesChanged( @Override public void onLost(Network network) { // This callback is called when a capability is lost. - - // e.g. user disconnected from wifi or disabled mobile data. - // If a user switches from wifi to mobile, - // onLost is called (for wifi), - - // then onAvailable is called afterwards (for mobile). - // Meaning the user receives [wifi] -> [none] -> [mobile] - - // If no other connectivity is present, onAvailable will not be called, - // and onLost is the only event that will happen. - // i.e. the user will only receive [wifi] -> [none] - sendEvent(List.of(CONNECTIVITY_NONE)); + // + // The provided Network object contains information about the + // network capability that has been lost, so we cannot use it. + // + // Instead, post the current network but with a delay long enough + // that we avoid a race condition. + sendCurrentStatusWithDelay(); } }; connectivity.getConnectivityManager().registerDefaultNetworkCallback(networkCallback); @@ -118,4 +111,11 @@ private void sendEvent(List networkTypes) { // Emit events on main thread mainHandler.post(runnable); } + + private void sendCurrentStatusWithDelay() { + Runnable runnable = () -> events.success(connectivity.getNetworkTypes()); + // Emit events on main thread + // 500 milliseconds to avoid race conditions + mainHandler.postDelayed(runnable, 500); + } } From 1a7c66d6858abe69acbbf5f2dbd8f43b0735f99a Mon Sep 17 00:00:00 2001 From: Miguel Beltran Date: Wed, 17 Apr 2024 09:15:01 +0200 Subject: [PATCH 10/11] update docs --- packages/connectivity_plus/connectivity_plus/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/connectivity_plus/connectivity_plus/README.md b/packages/connectivity_plus/connectivity_plus/README.md index 3537fb754a..de2c75c763 100644 --- a/packages/connectivity_plus/connectivity_plus/README.md +++ b/packages/connectivity_plus/connectivity_plus/README.md @@ -101,8 +101,6 @@ _`none` is supported on all platforms by default._ Connectivity changes are no longer communicated to Android apps in the background starting with Android O (8.0). You should always check for connectivity status when your app is resumed. The broadcast is only useful when your application is in the foreground. -To ensure connectivity changes on Android are tracked correctly, an event containing `[ConnectivityResult.none]` may be emitted in during connectivity changes. e.g. Going from WiFi to mobile network may emit: `[wifi] -> [none] -> [mobile]`. - ### iOS & MacOS On iOS simulators, the connectivity types stream might not update when Wi-Fi status changes. This is a known issue. From 32606f19e128bcf9813643f400ea36b3642e110e Mon Sep 17 00:00:00 2001 From: Miguel Beltran Date: Wed, 17 Apr 2024 09:16:54 +0200 Subject: [PATCH 11/11] update docs --- .../connectivity_plus/lib/connectivity_plus.dart | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/connectivity_plus/connectivity_plus/lib/connectivity_plus.dart b/packages/connectivity_plus/connectivity_plus/lib/connectivity_plus.dart index 8d6a277e11..cef5ddee13 100644 --- a/packages/connectivity_plus/connectivity_plus/lib/connectivity_plus.dart +++ b/packages/connectivity_plus/connectivity_plus/lib/connectivity_plus.dart @@ -41,12 +41,6 @@ class Connectivity { /// status changes, this is a known issue that only affects simulators. /// For details see https://github.com/fluttercommunity/plus_plugins/issues/479. /// - /// On Android, the Stream may emit [ConnectivityResult.none] after - /// losing connection, then emit another [ConnectivityResult] for the - /// new network. - /// e.g. going from wifi to mobile would emit: - /// wifi -> none -> mobile - /// /// The emitted list is never empty. In case of no connectivity, the list contains /// a single element of [ConnectivityResult.none]. Note also that this is the only /// case where [ConnectivityResult.none] is present.