0

I wrote a function in a Delphi Android app to determine if either Mobile or Wifi (or both) are enabled. I used Checking internet connection in android using getActiveNetworkInfo for reference.

I must have made a mistake, because the function isn't detecting Wifi, even when it is enabled on the phone!

function WhichNetwork: Integer; // 0 = none, 1 = wifi only, 2 = mobile only, 3 = both
var
  obj: JObject;
  cm: JConnectivityManager;
  networks: TJavaObjectArray<JNetwork>;
  i: Integer;
  network: JNetworkInfo;
  networktype: Integer;
  networkname: String;
begin
  Result := 0;
  obj := TAndroidHelper.Context.getSystemService(TJContext.JavaClass.CONNECTIVITY_SERVICE);
  if Assigned(obj) then
  begin
    cm := TJConnectivityManager.Wrap((obj as ILocalObject).GetObjectID);
    if Assigned(cm) then
    begin
      networks := cm.getAllNetworks;
      if Assigned(networks) then
      begin
        for i := 0 to networks.Length - 1 do // this looks right!
        begin
          network := cm.getNetworkInfo(i);
          networkname := JStringtoString(network.getTypeName); // for debug
          networktype := network.gettype;
          if networkType = TJConnectivityManager.JavaClass.TYPE_MOBILE then
          begin
            if network.isConnectedOrConnecting then
              Result := Result + 2;
          end
          else if networkType = TJConnectivityManager.JavaClass.TYPE_WIFI then
          begin
            if network.isConnectedOrConnecting then
              Result := Result + 1;
          end;
        end;
      end;
    end;
  end;
end;

After experimenting, I made the following change and it worked!

for i := 0 to networks.Length do // now this works

But, this is so not how Delphi usually works. Can someone explain why the Length of a TJavaObjectArray is zero-based?

It's an academic question, and I really want to understand this.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Freddie Bell
  • 2,186
  • 24
  • 43
  • The question you are linking states that `getAllNetworkInfo` is deprecated, maybe that is part of your issue? – nil Nov 20 '17 at 13:16
  • I'm not using getAllNetworkInfo! – Freddie Bell Nov 20 '17 at 13:17
  • Oh, sorry. I mixed up `getAllNetworks` and `getNetworkInfo` while reading your code. – nil Nov 20 '17 at 13:19
  • There's a big difference between those 2. – Freddie Bell Nov 20 '17 at 13:24
  • 1
    `getNetworkInfo (int networkType)` is deprecated, too. Documentation states you should use `getNetworkInfo (Network network)` instead. So I guess `network := cm.getNetworkInfo(networks[I]);`. If you do that, with a loop that _looks right_ do you get the expected results? – nil Nov 20 '17 at 13:34
  • networks.length is unaffected by using your suggestion. – Freddie Bell Nov 20 '17 at 13:37
  • I don't get what you mean. I wasn't suggesting that it would affect the length of anything. My idea is that you are using one or more calls in a wrong way - and using a deprecated function might be exactly that - so that you get a behavior that can be interpreted as something completely different. In your case an array that looks like a mix of zero based and one based. – nil Nov 20 '17 at 13:45
  • Let's try this in plain English: `networks` is assigned by `cm.getAllnetworks`. The length of `networks` is already determined at that point. What comes after that, has no affect on the length of `networks`. – Freddie Bell Nov 20 '17 at 13:49
  • 1
    No, and no effect on the array. You iterate over an array returned by `cm.getAllnetworks`, then proceed to call a deprecated function (`cm.getNetworkInfo`) with the loop variable. Where is the connection to the array at that point? You aren't even accessing it inside your loop. – nil Nov 20 '17 at 13:53
  • Quite. But I have to determine the range of values for i. – Freddie Bell Nov 20 '17 at 14:06
  • See the answer from @JRL, That's what I tried to verify with my suggested change. – nil Nov 20 '17 at 14:13
  • Yes, of course I understand now. Before that, not so much. – Freddie Bell Nov 20 '17 at 15:21
  • @getNetWorkInfo(int) should always return something. But the parameter is not an index, and that is why it probably worked. You should use the overload that takes a JNetWork as parameter, i.e. passing `networks[i]`, and then `for i := 0 to length-1` is correct. – Rudy Velthuis Nov 20 '17 at 15:49

2 Answers2

4

There is nothing wrong with this 0-based array.

You are calling the (deprecated) overload of getNetworkInfo that takes an integer argument. This integer specifies "which networkType in which you're interested", such as TYPE_MOBILE (0), TYPE_WIFI (1), and not an index position related to the array returned by getAllNetworks.

You need to call the getNetworkInfo overload that gets a Network as argument, as @nil said you in the comments.

for i := 0 to networks.Length - 1 do begin
    network := cm.getNetworkInfo(networks[i]);
    ...
end;
JRL
  • 3,363
  • 24
  • 36
3

By convention Delphi uses zero based indexes. Dynamic arrays are zero based. Strings are historically one based but on the mobile platforms the default strings is now zero based. Other types such as container classes, fixed length arrays, are zero or one based as determined by the author, but by convention, zero based is preferred.

for i := 0 to networks.Length do

You say that this loop is correct, but by all known conventions it looks like a mistake since the loop covers one element too many. It should be:

for i := 0 to networks.Length-1 do

However, your loop doesn't refer to networks in the loop body. Which makes one wonder why you use networks.Length at all if you ignore the elements of networks. If you are going to obtain the array networks then surely you should be using it. I expect that once you resolve this conundrum things will become clear.


You seem to be struggling to find the documentation. Perhaps you are looking for Delphi documentation. You won't find that. These are Android libraries, and you will need to look in the Android documentation. Start here: https://developer.android.com/reference/android/net/ConnectivityManager.html

You are calling getNetworkInfo. Let's read its documentation. There are two overloads, declared like this:

NetworkInfo getNetworkInfo(int networkType);

NetworkInfo getNetworkInfo(Network network);

The first overload takes a type parameter, but you are passing an integer index. That is a mistake. Further, the first overload is deprecated.

It looks very much like you intend to use the second overload and so the code should be:

var
  networks: TJavaObjectArray<JNetwork>;
  networkInfo: JNetworkInfo;
....
networks := cm.getAllNetworks;
for i := 0 to networks.Length-1 do
begin
  networkInfo := cm.getNetworkInfo(networks[i]);
  ....
end;

Anyway, the big lesson here for you is to stop using trial and error. Instead, read the documentation.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • But I just told you, with length -1, there is 1 element in the array (the wifi element) that doesn't get examined by the for loop. That's why I'm asking this question. – Freddie Bell Nov 20 '17 at 13:15
  • I'm not joking. There really are length - zero elements in this array! – Freddie Bell Nov 20 '17 at 13:23
  • Length - zero equals Length. But your loop iterates Length + 1 times. – David Heffernan Nov 20 '17 at 13:50
  • And yet, despite all that, the program finds that the element whose index is 0 is defined and contains information regarding the mobile network, and the element whose index = length of the array is also defined and suprizingly so, contains the information regarding the wifi network. Teamviewer me, and I'll show it to you! – Freddie Bell Nov 20 '17 at 13:51
  • If that is so, and you are using the library correctly, then you have something completely counter all known conventions. Is there documentation? – David Heffernan Nov 20 '17 at 13:54
  • That's why I asked the question you see? Otherwise I wouldn't have. All I can tell you is that it works - and my app is able to detect if WIFI is connected, when previously, it didn't. – Freddie Bell Nov 20 '17 at 13:57
  • Is there documentation? Why does the loop not refer to `networks`? – David Heffernan Nov 20 '17 at 14:01
  • It does. Are you being obtuse now? – Freddie Bell Nov 20 '17 at 14:04
  • No. I'm not. But I'm losing patience. Why so insolent? Is there documentation? Yes or no. Your loop body does not refer to `networks`. – David Heffernan Nov 20 '17 at 14:06
  • There is no documentation that guides a Delphi Android user in translating Java to Delphi. – Freddie Bell Nov 20 '17 at 14:08
  • There is documentation. You just have not found it yet. Or more likely, you aren't prepared to read it because it is in Java and you only read Pascal. – David Heffernan Nov 20 '17 at 14:08
  • You didn't even look yourself. Have we to do everything for you? Can we agree that your loop body does not refer to `networks`? – David Heffernan Nov 20 '17 at 14:09
  • @David: There are two overloaded functions `getNetworkInfo`. The first is deprecated and takes an Integer value, the second is *not* deprecated and takes a `JNetwork` parameter. I assume the second should be called , like in your example. So your code would probably compile, as it uses the second overload. Checked in D10.2 Tokyo. – Rudy Velthuis Nov 20 '17 at 14:36
  • 1
    @RudyVelthuis Hmm, I should have taken my own advice and read the documentation properly. Mea culpa. – David Heffernan Nov 20 '17 at 15:27
  • @David: I grepped the sources. – Rudy Velthuis Nov 20 '17 at 15:45