Problem with Smack in Facebook chat app for Android: Connection.getRoster().getEntries() is always empty

StackOverflow https://stackoverflow.com/questions/7170161

  •  11-01-2021
  •  | 
  •  

Question

I'm trying to implement a simple Facebook chat application on Android. It lets the user login, after which the app opens a new ListActivity enumerating the user's online contacts, and upon clicking on an item, another Activity opens where their conversation will take place.

The first screen where the user logs in is called MainActivity. It contains two EditTexts: one for the FB username, and the other for password. There are also two buttons: Clear, which empties the EditTexts, and OK, which upon click, executes the following AsyncTask via new LoginTask().execute():

class LoginTask extends AsyncTask<Void, Void, Void> {
   private ProgressDialog loading;

   @Override
   protected void onPreExecute() {
      loading = ProgressDialog.show(MainActivity.this, "", "Loading...");
   }

   @Override
   protected Void doInBackground(Void... params) {
      // setup XMPP connection
      ConnectionConfiguration config = new ConnectionConfiguration(
            "chat.facebook.com", 5222, "chat.facebook.com");
      config.setDebuggerEnabled(true);
      config.setSASLAuthenticationEnabled(true); // TRUE for fb
      conn = new XMPPConnection(config);

      try {
         // attempt login
         conn.connect();
         SASLAuthentication.supportSASLMechanism("PLAIN", 0);
         conn.login(login_field.getText().toString(),
               pwd_field.getText().toString(), "");

         Log.d("TRACE", "isAuthenticated? " + conn.isAuthenticated());

         // list online contacts
         Roster roster = conn.getRoster();
         Collection<RosterEntry> entries = roster.getEntries();
         Log.d("TRACE", "entries.size()=" + entries.size());
         for (RosterEntry e : entries) {
            Log.d("PRESENCE", e.getUser() + "=" + roster.getPresence(e.getUser()).isAvailable());
            if (roster.getPresence(e.getUser()).isAvailable()) {
               HashMap<String, String> contact = new HashMap<String, String>();
               contact.put(NAME_KEY, e.getName());
               contact.put(USERJID_KEY, e.getUser());
               Log.d("ADD", "NAME_KEY=" + e.getName() + " USERJID_KEY=" + e.getUser());
               contacts.add(contact);
            }
         }
         Collections.sort(contacts, new ContactComparator()); // sort alphabetically
         Log.d("TRACE", "MainActivity.contacts.size(): " + contacts.size());

         Intent in = new Intent(MainActivity.this, ContactList.class);
         startActivity(in);
      } catch (Exception e) {
         Log.d("EXCEPTION", e.toString());
      }

      return null;
   }

   @Override
   protected void onPostExecute(Void result) {
      loading.dismiss();
   }
}

My problem is that whenever the second screen is opened, the ListView often doesn't show anythying, BUT IT SOMETIMES DOES. I decided to add log messages to the part where I'm retrieving the contacts via conn.getRoster() and I found out that most of the time, a call to conn.getRoster().getEntries.size() returns a zero, but when I actually login to Facebook via browser, I can see that I do have online contacts. What I don't understand, though, is why it sometimes returns the correct number, but only once in almost every billion times I run the application.

Somebody help, please? This app's due in four hours and I don't know what to do anymore since there seems to be nothing wrong with my code.

Was it helpful?

Solution

I found the answer to this question the day after I posted it here. Apparently, when conn.login() is called, the user becomes authenticated and the Facebook server starts sending the roster items. The reason why nothing appears in the second screen most of the time is that the code reaches the call to startActivity() EVEN BEFORE the Facebook server is done sending the packets containing the roster items. Sometimes, though, the server is fast enough to send the packets but startActivity() is called and adding to the ArrayList becomes interrupted, resulting to an incomplete ListView of your online contacts. A workaround I did was to call Thread.sleep() after conn.login() and create a 5- to 10-second delay so that the server would have enough time to send all the packets.

That worked only for a while but when I has the added task of retrieving even the photos of my contacts (using VCard), even a 30-second delay wasn't enough. You should find a way then to determine when the server is done sending packets and the VCards and proceed with adding to the ArrayList only when you get the signal.

Unfortunately, looking at the Smack documentation, there doesn't seem to be a way to do that. But that's because my goal was, overall, wrong. What I was trying to do is this:

  1. Log in to Facebook.
  2. Get all online contacts.
  3. Put them in an ArrayList.
  4. Open a ListActivity.
  5. Display the ArrayList contents in the ListActivity.

When it should be doing this:

  1. Log in to Facebook.
  2. Open a ListActivity.
  3. Get ALL your contacts.
  4. Dynamically update the ListView depending on your contacts' presence (still haven't done this part).

In short, the Smack API has been designed to handle real-time events, and because it's an instant messaging library, I don't think it should have been designed otherwise. What I was trying to do in the first example was rather static.

OTHER TIPS

Maybe you also consifer roster.reloadAndAwait()

public void getRoaster(final Callback<Collection<RosterEntry>> callback) {
    final Roster roster = Roster.getInstanceFor(connection);
    if (!roster.isLoaded())
    try {
        roster.reloadAndWait();
    } catch (SmackException.NotLoggedInException | SmackException.NotConnectedException | InterruptedException e) {
        e.printStackTrace();
    }

    Collection<RosterEntry> entries = roster.getEntries();
    for (RosterEntry entry : entries) {
        android.util.Log.d(AppConstant.PUBLIC_TAG, entry.getName());
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top