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.
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 tostartActivity()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 butstartActivity()is called and adding to theArrayListbecomes interrupted, resulting to an incompleteListViewof your online contacts. A workaround I did was to callThread.sleep()afterconn.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:
ArrayList.ListActivity.ArrayListcontents in theListActivity.When it should be doing this:
ListActivity.ListViewdepending 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.