Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: registerWifiNetwork for Android 10 and proper result returned #318

Open
wants to merge 14 commits into
base: master
Choose a base branch
from

Conversation

richirisu
Copy link

Method registerWifiNetworkDeprecated is not working on Android 10, and instead an exception is raised. The reason is that WifiManager.addNetwork has been deprecated and will always return -1 on Android 10.

Unfortunately, the intent android.provider.Settings.ACTION_WIFI_ADD_NETWORKS is only available from Android 11 onwards. However, by calling WifiManager.addNetworkSuggestions a message will be shown in the system notification area, from where the network can then by added.

Please, have a look at the following Stackoverflow link for more information. There is also a screenshot for how the notification looks like.
https://stackoverflow.com/questions/59753528/creating-a-custom-wifi-setup

Note, that uninstalling the application might cause the network to be removed from the registered networks list.

This solves issue #315

@richirisu richirisu changed the title Fix for method registerWifiNetwork for deprecated API on Android 10 fix: Fix for method registerWifiNetwork for deprecated API on Android 10 Dec 27, 2022
@daadu
Copy link
Member

daadu commented Dec 29, 2022

@richirisu Currently, your changes (WifiManager.addNetworkSuggestions) are implemented under the connect method. It seems redundant to me. Have you given it a try? The registerNetwork - as of now, is kept to have "system-wide" connectivity - irrespective if what app is open, installed, etc.

Let me know what you think about it.

@richirisu
Copy link
Author

Hello, thanks for your response. I had a look into the connect method. However, I see two problems here.

(1) Currently, the registerWifiNetwork method throws an unexpected exception on Android 10. I think, it's a good idea to fix at least this circumstance.

I don't recommend it, but a minimum fix could be to simply return false in case of Android 10. Something like the following.

if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
  poResult.success(null);
}

That reminds me, is there actually a reason that poResult.success is called with null and not a bool, e.g. false in this case?

(2) The other issue I see is that the connect method intends to directly connect with a network and that its purpose is not primarily to register a network permanently.

Anyway, I could confirm that a call to the connect method does indeed do what you have described. However, I had to set the two parameters for joinOnce to false, and withInternet to true; otherwise, WifiManager.addNetworkSuggestions would not have been called internally.

With that information I would suggest the following fix for the registerWifiNetwork method instead of my previous commit.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
   --- no changes here ---
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
  connectTo(
      poResult,
      ssid,
      bssid,
      password,
      security,
      false, // joinOnce,
      true, // withInternet,
      isHidden,
      0); // timeoutInSeconds
} else {
   --- no changes here either ---
}

Let me know what you think and if I should update my pull request accordingly.

@daadu
Copy link
Member

daadu commented Sep 4, 2023

Sorry, for late review!! The changes sounds good to me.

The Point 1 - you mentioned, let's just fix that as of now - your current commit does that.

@daadu
Copy link
Member

daadu commented Sep 4, 2023

@richirisu as per your suggestion, I have made changes that will return proper result as well - please review those changes. After which I will merge this PR.

@daadu daadu changed the title fix: Fix for method registerWifiNetwork for deprecated API on Android 10 fix: registerWifiNetwork for Android 10 and proper result returned Sep 4, 2023
@richirisu
Copy link
Author

richirisu commented Oct 11, 2023

Hey thanks for the efforts of looking into this. Really appreciated.

I didn't find any issues regarding the code. I only fixed some minor spelling mistakes. That's all.

@aminebagga
Copy link

Hi all, I tested this branch with android sdk version 33
When I call registerWifiNetwork it hangs !
I added logs to debug thsi function, I can confirm that moActivity.startActivityForResult was called but the callback onActivityResult was never fired.
And in the file WiFiFlutter/packages/wifi_iot/lib/wifi_iot.dart : registerWifiNetwork, _channel.invokeMethod hangs and never return
BUT
In file WiFiFlutter/packages/wifi_iot/android/src/main/java/com/alternadom/wifiiot/WifiIotPlugin.java : Function onMethodCall, registerWifiNetwork(poCall, poResult); was called and did not hang
I wonder where it hangs as I did not see errors at all

@aminebagga
Copy link

I also tested registerWifiNetwork on two differents AP with and without internet
WiFiForIoTPlugin.connect (withInternet = true) and WiFiForIoTPlugin.connect (withInternet = false) was called before registerWifiNetwork accordingtly
In both cases registerWifiNetwork hangs and never return

@richirisu
Copy link
Author

I have been testing this issue on a Pixel phone on Android 13 and I can confirm that the method blocks if awaited for.

I looked into it and it is not a Flutter issue. On the Android side moActivity.startActivityForResult works as expected and does not block either.

https://github.com/richirisu/WiFiFlutter/blob/080c0ef02e5558c632342a4adcb565ea7c4c43e3/packages/wifi_iot/android/src/main/java/com/alternadom/wifiiot/WifiIotPlugin.java#L1006

However, onActivityResult is not being invoked later on, and as such poResult is never being resolved.

https://github.com/richirisu/WiFiFlutter/blob/080c0ef02e5558c632342a4adcb565ea7c4c43e3/packages/wifi_iot/android/src/main/java/com/alternadom/wifiiot/WifiIotPlugin.java#L254

@aminebagga
Copy link

Do you know if this is working fine with android 14 ? I do not have pixel telephone to test with android 14

@daadu
Copy link
Member

daadu commented Oct 31, 2023

There might be a bug in the PR. Will fix it in upcoming weeked.

@aminebagga
Copy link

great ! waiting for your fix then.
Thanks @daadu

@daadu
Copy link
Member

daadu commented Oct 31, 2023

I have fixed it, please check @aminebagga @richirisu

@aminebagga
Copy link

Now onActivityResult is immediatly called with resultCode Activity.RESULT_CANCELED without waiting for the save network dialog and so registerWifiNetwork always return false

@richirisu
Copy link
Author

richirisu commented Nov 1, 2023

onActivityResult is now being called. Thanks for the fix :)

Unfortunately, aminebagga is correct: either way, if I accept the suggested networks or cancel the dialog, the intent's result always defaults to RESULT_CANCELED somehow. That's very strange.

I see no wrong-doing in the source code. It matches the official developer guide.

https://developer.android.com/develop/connectivity/wifi/wifi-save-network-passpoint-config

@aminebagga
Copy link

@richirisu @daadu if we comment the line "intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);" all will work fine.
I have some suggestions to improve the code, as we should be able to distinguish the case of already registered network and a new network, in both cases the return result will be "true" but for a new network, the android system will connect to the requested network whereas for an already registered one it will not.
So I suggest to add theses changes

diff --git a/packages/wifi_iot/android/src/main/java/com/alternadom/wifiiot/WifiIotPlugin.java b/packages/wifi_iot/android/src/main/java/com/alternadom/wifiiot/WifiIotPlugin.java
index a870851..30d02d5 100644
--- a/packages/wifi_iot/android/src/main/java/com/alternadom/wifiiot/WifiIotPlugin.java
+++ b/packages/wifi_iot/android/src/main/java/com/alternadom/wifiiot/WifiIotPlugin.java
@@ -93,6 +93,11 @@ public class WifiIotPlugin
   private static final Map<Integer, Result> resultMap = new HashMap<Integer, Result>();
   private static final int ACTIVITY_RESULT_REQUEST_CODE_ADD_NETWORKS = 66778899;
 
+  // Register network reuslts
+  private static final int WIFI_REGISTER_NETWORK_RESULT_SUCCESS = 0;
+  private static final int WIFI_REGISTER_NETWORK_RESULT_FAILED = 1;
+  private static final int WIFI_REGISTER_NETWORK_RESULT_ALREADY_EXISTS = 2;
+
   // initialize members of this class with Context
   private void initWithContext(Context context) {
     moContext = context;
@@ -258,7 +263,25 @@ public class WifiIotPlugin
     if (resultMap.containsKey(requestCode)) {
       final Result result = resultMap.get(requestCode);
       if (requestCode == ACTIVITY_RESULT_REQUEST_CODE_ADD_NETWORKS) {
-        result.success(resultCode == Activity.RESULT_OK);
+        if ((data != null) && data.hasExtra(android.provider.Settings.EXTRA_WIFI_NETWORK_RESULT_LIST)) {
+          for (int code : data.getIntegerArrayListExtra(android.provider.Settings.EXTRA_WIFI_NETWORK_RESULT_LIST)) {
+            if (code == android.provider.Settings.ADD_WIFI_RESULT_SUCCESS) {
+              result.success(WIFI_REGISTER_NETWORK_RESULT_SUCCESS);
+              break;
+            }
+            else if (code == android.provider.Settings.ADD_WIFI_RESULT_ALREADY_EXISTS) {
+              result.success(WIFI_REGISTER_NETWORK_RESULT_ALREADY_EXISTS);
+              break;
+            }
+            else {
+              result.success(WIFI_REGISTER_NETWORK_RESULT_FAILED);
+              break;
+            }
+          }
+        }
+        else {
+          result.success(WIFI_REGISTER_NETWORK_RESULT_FAILED);
+        }
       }
       resultMap.remove(requestCode);
       return true;
@@ -1004,11 +1027,10 @@ public class WifiIotPlugin
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
         Intent intent = new Intent(android.provider.Settings.ACTION_WIFI_ADD_NETWORKS);
         intent.putExtras(bundle);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         if (moActivity != null) {
-          moActivity.startActivityForResult(intent, ACTIVITY_RESULT_REQUEST_CODE_ADD_NETWORKS);
           // listen for activity result
           resultMap.put(ACTIVITY_RESULT_REQUEST_CODE_ADD_NETWORKS, poResult);
+          moActivity.startActivityForResult(intent, ACTIVITY_RESULT_REQUEST_CODE_ADD_NETWORKS);
         } else {
           poResult.error(
               "NoActivityError",
diff --git a/packages/wifi_iot/lib/wifi_iot.dart b/packages/wifi_iot/lib/wifi_iot.dart
index 6c435fb..0063f20 100755
--- a/packages/wifi_iot/lib/wifi_iot.dart
+++ b/packages/wifi_iot/lib/wifi_iot.dart
@@ -391,9 +391,11 @@ class WiFiForIoTPlugin {
   ///
   /// @param [isHidden] Whether the SSID is hidden (not broadcasted by the AP).
   ///
-  /// @returns True in case the requested network could be registered, false
-  ///   otherwise.
-  static Future<bool> registerWifiNetwork(
+  /// @returns :
+  /// 0 = in case the requested network could be registered.
+  /// 1 = if failed to register.
+  /// 2 = if the network is already registered.
+  static Future<int> registerWifiNetwork(
     String ssid, {
     String? bssid,
     String? password,
@@ -409,11 +411,11 @@ class WiFiForIoTPlugin {
     // TODO: support any binary sequence as required instead of just strings.
     if (ssid.length == 0 || ssid.length > 32) {
       print("Invalid SSID");
-      return false;
+      return 1;
     }
 
     if (!Platform.isIOS && !await isEnabled()) await setEnabled(true);
-    bool? bResult;
+    int? bResult;
     try {
       bResult = await _channel.invokeMethod('registerWifiNetwork', {
         "ssid": ssid.toString(),
@@ -425,7 +427,7 @@ class WiFiForIoTPlugin {
     } on MissingPluginException catch (e) {
       print("MissingPluginException : ${e.toString()}");
     }
-    return bResult ?? false;
+    return bResult ?? 1;
   }
 
   /// Scan for Wi-Fi networks and connect to the requested AP Wi-Fi network if

@richirisu
Copy link
Author

Hey, good to know that there is a way to make it work.

Personally, I think a return value of true/false is enough, but if you are going for a more specific return value, I would suggest to use an enum on the Flutter side instead of pure integers. I noticed that the Android values use 0 in case of success. Technically, that's OK.

Android

ADD_WIFI_RESULT_SUCCESS = 0
ADD_WIFI_RESULT_ADD_OR_UPDATE_FAILED = 1
ADD_WIFI_RESULT_ALREADY_EXISTS = 2

Flutter

enum RegisterWifiNetworkResult{ success, failed, alreadyRegistered }

By the way, is iOS unaffected?

@aminebagga
Copy link

Hi @richirisu I did not tested IOS. I'm OK with you for the use of enum

aminebagga and others added 3 commits December 8, 2023 17:54
…sible values.

Handle wifi dialog notifications.
Return register operation result as int instead of boolean with 3 pos…
@richirisu
Copy link
Author

Just a short notice but I have merged the suggestions from @aminebagga into this pull request.

@z1u24
Copy link

z1u24 commented Feb 2, 2024

If alreadyRegistered is returned but it does not connect to WiFi, how can I connect to a registered device

@aminebagga
Copy link

If alreadyRegistered is returned but it does not connect to WiFi, how can I connect to a registered device

You have to open settings, you can use WiFiForIoTPlugin.setEnabled(true, shouldOpenSettings: true), and ask user to click on the registered wifi

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants