BlackBerry Refresh location via GPS or Cell Tower

869 views Asked by At

I am trying to refresh the current location through a click of a button. The location can be acquired via GPS or Cell Tower, whichever is available. My problem is I never see the "Loading Screen". I know it appears as something appears/closes instantly while the coordinates remain zero. Can somebody help me - what I am doing wrong below?

Neither the location is updated nor the loading screen appears. Without the loading screen and by multiple clicks of the "refresh button" I do get the location. Below is my code for handling clicks on the "Refresh Button":

FieldChangeListener refreshImgListener = new FieldChangeListener() {   
    public void fieldChanged(Field field, int context) 
    {
        Thread backgroundWorker = new Thread(new Runnable() {
            public void run() {
                refreshCoordinates();
            }
        });
        busyDialog.setEscapeEnabled(false);  
        busyDialog.show();
        backgroundWorker.start();
    }
};

And my refreshCoordinates() method is as below:

public void refreshCoordinates() {
    do
    {
        getLatitude(handleeGPS.latitude);
        getLongitude(handleeGPS.longitude);
    } while ((longi == "0.0" || lati == "0.0") || (longi.length() == 0 || lati.length()==0));

    UiApplication.getUiApplication().invokeLater( new Runnable()
    {
        public void run ()
        {
            lblLatitude.setText(lati);
            lblLongitude.setText(longi);
            busyDialog.cancel();
        }
    } );
}

public static String getLatitude(double value) 
{
    lati= Double.toString(value);
    return lati;
}
public static String getLongitude(double value) 
{
    longi= Double.toString(value);
    return longi;
}

Class that returns the latitude and longitude values:

public class handleeGPS{

    static GPSThread gpsThread;
    public static double latitude;
    public static double longitude;

    public handleeGPS(){
        gpsThread = new GPSThread();
        gpsThread.start();
    }

    private static class GPSThread extends Thread{
        public void run() {
            Criteria myCriteria = new Criteria();
            myCriteria.setCostAllowed(false);
            int m_bbHandle = CodeModuleManager.getModuleHandle("net_rim_bb_lbs");
            if(m_bbHandle>0){
                try {
                    int cellID = GPRSInfo.getCellInfo().getCellId();
                    int lac = GPRSInfo.getCellInfo().getLAC();
                    String urlString2 = "http://www.google.com/glm/mmap";
                    // Open a connection to Google Maps API
                    ConnectionFactory connFact = new ConnectionFactory();
                    ConnectionDescriptor connDesc;
                    connDesc = connFact.getConnection(urlString2);
                    HttpConnection httpConn2;
                    httpConn2 = (HttpConnection)connDesc.getConnection();
                    httpConn2.setRequestMethod("POST");
                    // Write some custom data to Google Maps API
                    OutputStream outputStream2 = httpConn2.openOutputStream();//getOutputStream();
                    WriteDataGoogleMaps(outputStream2, cellID, lac);
                    // Get the response
                    InputStream inputStream2 = httpConn2.openInputStream();//getInputStream();
                    DataInputStream dataInputStream2 = new DataInputStream(inputStream2);
                    // Interpret the response obtained
                    dataInputStream2.readShort();
                    dataInputStream2.readByte();
                    int code = dataInputStream2.readInt();
                    //Dialog.alert(code+"");
                    if (code == 0) {
                        latitude= dataInputStream2.readInt() / 1000000D;
                        longitude=dataInputStream2.readInt() / 1000000D;
                        //Dialog.alert(latitude+"-----"+longitude);
                        dataInputStream2.readInt();
                        dataInputStream2.readInt();
                        dataInputStream2.readUTF();
                    } else {
                        System.out.println("Error obtaining Cell Id ");
                    }
                    outputStream2.close();
                    inputStream2.close();
                } catch (Exception e) {
                    System.out.println("Error: " + e.getMessage());
                }
            } else {
                try {
                    LocationProvider myLocationProvider = LocationProvider.getInstance(myCriteria);
                    try {
                        Location myLocation = myLocationProvider.getLocation(300);
                        latitude  = myLocation.getQualifiedCoordinates().getLatitude();
                        longitude = myLocation.getQualifiedCoordinates().getLongitude();
                        if(latitude==0.0 && longitude==0.0){
                            try {
                                int cellID = GPRSInfo.getCellInfo().getCellId();
                                int lac = GPRSInfo.getCellInfo().getLAC();
                                String urlString2 = "http://www.google.com/glm/mmap";
                                // Open a connection to Google Maps API
                                ConnectionFactory connFact = new ConnectionFactory();
                                ConnectionDescriptor connDesc;
                                connDesc = connFact.getConnection(urlString2);
                                HttpConnection httpConn2;
                                httpConn2 = (HttpConnection)connDesc.getConnection();
                                httpConn2.setRequestMethod("POST");
                                // Write some custom data to Google Maps API
                                OutputStream outputStream2 = httpConn2.openOutputStream();
                                //getOutputStream();
                                WriteDataGoogleMaps(outputStream2, cellID, lac);
                                // Get the response
                                InputStream inputStream2 = httpConn2.openInputStream();
                                //getInputStream();
                                DataInputStream dataInputStream2 = new DataInputStream(inputStream2);
                                // Interpret the response obtained
                                dataInputStream2.readShort();
                                dataInputStream2.readByte();
                                int code = dataInputStream2.readInt();
                                //Dialog.alert(code+"");
                                if (code == 0) {
                                    latitude= dataInputStream2.readInt() / 1000000D;
                                    longitude=dataInputStream2.readInt() / 1000000D;
                                    //Dialog.alert(latitude+"-----"+longitude);
                                    dataInputStream2.readInt();
                                    dataInputStream2.readInt();
                                    dataInputStream2.readUTF();
                                } else {
                                    System.out.println("Error obtaining Cell Id ");
                                }
                                outputStream2.close();
                                inputStream2.close();
                            } catch (Exception e) {
                                System.out.println("Error: " + e.getMessage());
                            }
                        }
                    }
                    catch ( InterruptedException iex ) {
                        return;
                    }
                    catch ( LocationException lex ) {
                        return;
                    }
                } catch ( LocationException lex ) {
                    return;
                }
            }
            return;
        }
    }

    private static void WriteDataGoogleMaps(OutputStream out, int cellID, int lac)
    throws IOException {
        DataOutputStream dataOutputStream = new DataOutputStream(out);
        dataOutputStream.writeShort(21);
        dataOutputStream.writeLong(0);
        dataOutputStream.writeUTF("en");
        dataOutputStream.writeUTF("Android");
        dataOutputStream.writeUTF("1.0");
        dataOutputStream.writeUTF("Web");
        dataOutputStream.writeByte(27);
        dataOutputStream.writeInt(0);
        dataOutputStream.writeInt(0);
        dataOutputStream.writeInt(3);
        dataOutputStream.writeUTF("");
        dataOutputStream.writeInt(cellID);
        dataOutputStream.writeInt(lac);
        dataOutputStream.writeInt(0);
        dataOutputStream.writeInt(0);
        dataOutputStream.writeInt(0);
        dataOutputStream.writeInt(0);
        dataOutputStream.flush();
    }
}
3

There are 3 answers

17
Nate On BEST ANSWER

Ok, so although my original answer was valid, the new code you posted has some different problems, so I'm posting a second answer. There were enough things that didn't look right, that I just rewrote your handleeGPS class. I'll explain the major changes I made, one-by-one:

  1. Try to use Java naming conventions. This makes it easier for us to help you. Before you posted the code to your handleeGPS class, I thought it was a variable, because lowercase names are usually used for variables, not classes.

  2. Avoid duplicating code. The handleeGPS class had a lot of code to read through, but most of it was the code to get location from Google's web service, which you duplicated in two places. Just make a method that contains only that code, and call it twice.

  3. I renamed your handleeGPS class to GPSHandler. I'm not sure if handlee was an error, or if that's a word in another language that you used. Anyway, the name should at least start with an uppercase letter.

  4. Avoid lots of static variables and methods. Sometimes, there really should be only one of something. A GPS handling class is probably a good example of that, because the device only has one GPS system. But, to enforce this code construct, don't mark everything as static. Just make the class a Singleton, which involves creating only one static member variable (_instance) and one static method (getInstance()). In my code, you will access the class like this: GPSHandler gps = GPSHandler.getInstance();.

  5. I believe the check you had for whether BB maps was installed was actually backwards. You looked up the net_rim_bb_lbs module, and if it was greater than zero (which means BB Maps is installed) then you went directly to the Google webservice. I think you want it the other way around (try device GPS if BB Maps installed). Also, since 6.0, you need to check for net_rim_bb_maps, too.

  6. Before you posted the update, I thought your getLatitude() and getLongitude() methods were actually fetching the device location. That was a bad assumption on my part. They were just converting numbers to strings. So, there's no reason for that to be done in the background (with a Thread). You already wrote your handleeGPS class to use a background thread, which is good. One background thread is enough. The UI that uses the location information should not also need a background Thread. I changed the code to add a GPSListener interface. That interface should be implemented by your UI code, to receive location updates. There is no reason to keep looping, asking if the location is not equal to {0.0, 0.0}. That's inefficient. With my code, you will just get notified when the location does change.

  7. The original code was not thread safe. The handleeGPS latitude and longitude variables were set on the background thread, and accessed on the UI thread. That's not safe. Two threads should not be reading and writing the same piece of data at once. By changing the code to push location data to the GPSListener, it avoids this problem.

  8. I uncommented the Dialog.alert() code you had inside your handleeGPS class, which would not have worked for you, because you're not allowed to make UI calls from the background. I surrounded those calls with UiApplication.getUiApplication().invokeLater() to make them safe.

To use this class, in your UI code somewhere, you would do this, instead of using a Thread to run your refreshCoordinates() method:

public void fieldChanged(Field field, int context) 
    // this is called when your location refresh button is clicked
    GPSHandler.getInstance().setListener(this); 
    GPSHandler.getInstance().requestLocationUpdates();
    busyDialog.setEscapeEnabled(false);  
    busyDialog.show();
}

...

public void onLocationReceived(Coordinates location) {
    lblLatitude.setText(Double.toString(location.getLatitude()));               
    lblLongitude.setText(Double.toString(location.getLongitude()));               
    busyDialog.cancel();               
}     

Make sure the class where you put that code (above) also implements GPSListener, which is an interface, defined here:

public interface GPSListener {
    public void onLocationReceived(Coordinates location);
}

and finally, the GPSHandler:

public class GPSHandler { 

   private GPSThread _gpsThread;
   private Coordinates _location;
   private boolean _gotLocation;
   private GPSListener _listener;

   /** this class will be a Singleton, as the device only has one GPS system */
   private static GPSHandler _instance;

   /** @return the Singleton instance of the GPSHandler */
   public static GPSHandler getInstance() {
      if (_instance == null) {
         _instance = new GPSHandler();
      }
      return _instance;
   }

   /** not publicly accessible ... use getInstance() */
   private GPSHandler() { 
   }

   /** call this to trigger a new location fix */
   public void requestLocationUpdates() {
       if (_gpsThread == null || !_gpsThread.isAlive()) {
          _gpsThread = new GPSThread();
          _gpsThread.start(); 
       }
   }

   public void setListener(GPSListener listener) {
      // only supports one listener this way
      _listener = listener;   
   }

   private void setLocation(final Coordinates value) {
      _location = value;
      if (value.getLatitude() != 0.0 || value.getLongitude() != 0.0) {
         _gotLocation = true;
         if (_listener != null) {
            // this assumes listeners are UI listeners, and want callbacks on the UI thread:
            UiApplication.getUiApplication().invokeLater(new Runnable() {
               public void run() {
                  _listener.onLocationReceived(value);
               }
            });
         }
      }
   }

   private class GPSThread extends Thread {

      private void getLocationFromGoogle() {
         try { 
            int cellID = GPRSInfo.getCellInfo().getCellId(); 
            int lac = GPRSInfo.getCellInfo().getLAC(); 

            String urlString2 = "http://www.google.com/glm/mmap"; 

            // Open a connection to Google Maps API  
            ConnectionFactory connFact = new ConnectionFactory(); 
            ConnectionDescriptor connDesc; 
            connDesc = connFact.getConnection(urlString2); 

            HttpConnection httpConn2; 
            httpConn2 = (HttpConnection)connDesc.getConnection(); 
            httpConn2.setRequestMethod("POST"); 

            // Write some custom data to Google Maps API  
            OutputStream outputStream2 = httpConn2.openOutputStream();//getOutputStream(); 
            writeDataGoogleMaps(outputStream2, cellID, lac); 

            // Get the response   
            InputStream inputStream2 = httpConn2.openInputStream();//getInputStream(); 
            DataInputStream dataInputStream2 = new DataInputStream(inputStream2); 

            // Interpret the response obtained  
            dataInputStream2.readShort(); 
            dataInputStream2.readByte(); 

            final int code = dataInputStream2.readInt(); 
            UiApplication.getUiApplication().invokeLater(new Runnable() {
               public void run() {
                  Dialog.alert(code + "");   
               }
            });                        

            if (code == 0) { 
               final double latitude = dataInputStream2.readInt() / 1000000D; 
               final double longitude = dataInputStream2.readInt() / 1000000D; 
               setLocation(new Coordinates(latitude, longitude, 0.0f));

               UiApplication.getUiApplication().invokeLater(new Runnable() {
                  public void run() {
                     Dialog.alert(latitude+"-----"+longitude);   
                  }
               });

               dataInputStream2.readInt(); 
               dataInputStream2.readInt(); 
               dataInputStream2.readUTF(); 
            } else { 
               System.out.println("Error obtaining Cell Id "); 
            } 
            outputStream2.close(); 
            inputStream2.close(); 
         } catch (Exception e) { 
            System.out.println("Error: " + e.getMessage()); 
         } 
      }

      private void tryGetLocationFromDevice() {
         _gotLocation = false;
         try {
            Criteria myCriteria = new Criteria(); 
            myCriteria.setCostAllowed(false); 
            LocationProvider myLocationProvider = LocationProvider.getInstance(myCriteria); 

            try { 
               Location myLocation = myLocationProvider.getLocation(300); 
               setLocation(myLocation.getQualifiedCoordinates()); 
            } catch ( InterruptedException iex ) { 
               System.out.println(iex.getMessage());
            } catch ( LocationException lex ) { 
               System.out.println(lex.getMessage());
            } 
         } catch ( LocationException lex ) { 
            System.out.println(lex.getMessage());
         }

         if (!_gotLocation) { 
            getLocationFromGoogle();
         } 
      }

      public void run() { 
         int bbMapsHandle = CodeModuleManager.getModuleHandle("net_rim_bb_lbs");    // OS < 6.0
         int bbMapsHandle60 = CodeModuleManager.getModuleHandle("net_rim_bb_maps"); // OS 6.0+
         if (bbMapsHandle > 0 || bbMapsHandle60 > 0) { 
            tryGetLocationFromDevice();
         } else {
            getLocationFromGoogle();
         }
      } 
   } 

   private void writeDataGoogleMaps(OutputStream out, int cellID, int lac) throws IOException { 
      DataOutputStream dataOutputStream = new DataOutputStream(out); 
      dataOutputStream.writeShort(21); 
      dataOutputStream.writeLong(0); 
      dataOutputStream.writeUTF("en"); 
      dataOutputStream.writeUTF("Android"); 
      dataOutputStream.writeUTF("1.0"); 
      dataOutputStream.writeUTF("Web"); 
      dataOutputStream.writeByte(27); 
      dataOutputStream.writeInt(0); 
      dataOutputStream.writeInt(0); 
      dataOutputStream.writeInt(3); 
      dataOutputStream.writeUTF(""); 

      dataOutputStream.writeInt(cellID); 
      dataOutputStream.writeInt(lac); 

      dataOutputStream.writeInt(0); 
      dataOutputStream.writeInt(0); 
      dataOutputStream.writeInt(0); 
      dataOutputStream.writeInt(0); 
      dataOutputStream.flush(); 
   } 

} 
8
Eugen Martynov On

I think thee is simple answer is error here:

((longi == "0.0" || lati == "0.0") || (longi.length() == 0 || lati.length()==0));

You have to use String.equals() instead of == operator.

After first call longi and lati have "0.0" value. But == will return false because it compare references by default and they are different because it's different objects.

9
Nate On

There's a lot of code that we can't see (e.g. getLatitude(), getLongitude(), refreshDetails()). So, there could be something going wrong there. Also, I don't see any Loading Screen in the code you posted, so I can't say why that isn't showing.

But, here's something that doesn't look right:

        synchronized (Application.getEventLock())
        {
            busyDialog.show();
        }

If you read this BlackBerry forum question, you'll see that trying to synchronize on the application event lock from the main (UI) thread can cause your app to freeze. The public void fieldChanged(Field field, int context) method is always called on the UI thread, because it's the UI thread that monitors buttons for clicks, and calls back your click handlers, like fieldChanged().

You can also read the BlackBerry API docs for Application, that explain that getEventLock() is for worker (also known as background) threads, not the main (aka UI) thread.

So, there's no need to use special techniques to get the event lock, in code that already runs on the UI thread. Instead of the code above, just do this:

            busyDialog.show();

Both of these techniques:

        synchronized (Application.getEventLock())
        {
            busyDialog.show();
        }

or

        UiApplication.getUiApplication().invokeLater(new Runnable() {
            public void run() {
               busyDialog.show();  
            }
        });

are ways to safely call UI methods from a background thread. But, you shouldn't use those in code that you know is running on the UI thread already.

Try fixing that, and see if your problem disappears.

Edit: also, your code is checking for a username and password before refreshing the location. Is that really what you want? I don't think this has anything to do with your problem, but normally, I wouldn't expect to need a username or password to access location services. Of course, I don't know your application, so this is really just a comment on my part.