reworked account managment. now status display actually works

This commit is contained in:
Daniel Gultsch 2014-02-04 15:09:50 +01:00
parent 14a171b088
commit 0d80d88736
8 changed files with 287 additions and 160 deletions

View File

@ -35,16 +35,16 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Status: " android:text="Status: "
android:textStyle="bold" android:textStyle="bold"
android:textSize="14sp" /> android:textSize="16sp" />
<TextView <TextView
android:id="@+id/account_status" android:id="@+id/account_status"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textColor="#669900" android:textColor="#669900"
android:text="Online" android:text="unknown"
android:textStyle="bold" android:textStyle="bold"
android:textSize="14sp"/> android:textSize="16sp"/>
</LinearLayout> </LinearLayout>

View File

@ -18,11 +18,20 @@ public class Account extends AbstractEntity{
public static final int OPTION_USETLS = 0; public static final int OPTION_USETLS = 0;
public static final int STATUS_OFFLINE = 0;
public static final int STATUS_ONLINE = 1;
public static final int STATUS_UNAUTHORIZED = 2;
public static final int STATUS_NOINTERNET = 3;
public static final int STATUS_TLS_ERROR = 4;
public static final int STATUS_SERVER_NOT_FOUND = 5;
protected String username; protected String username;
protected String server; protected String server;
protected String password; protected String password;
protected int options; protected int options;
protected String rosterVersion; protected String rosterVersion;
protected String resource;
protected int status = 0;
protected boolean online = false; protected boolean online = false;
@ -70,8 +79,16 @@ public class Account extends AbstractEntity{
this.password = password; this.password = password;
} }
public boolean isOnline() { public void setStatus(int status) {
return online; this.status = status;
}
public int getStatus() {
return this.status;
}
public void setResource(String resource) {
this.resource = resource;
} }
public String getJid() { public String getJid() {

View File

@ -1,6 +1,5 @@
package de.gultsch.chat.services; package de.gultsch.chat.services;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.List; import java.util.List;
@ -10,7 +9,7 @@ import de.gultsch.chat.entities.Contact;
import de.gultsch.chat.entities.Conversation; import de.gultsch.chat.entities.Conversation;
import de.gultsch.chat.entities.Message; import de.gultsch.chat.entities.Message;
import de.gultsch.chat.persistance.DatabaseBackend; import de.gultsch.chat.persistance.DatabaseBackend;
import de.gultsch.chat.ui.ConversationActivity; import de.gultsch.chat.ui.OnAccountListChangedListener;
import de.gultsch.chat.ui.OnConversationListChangedListener; import de.gultsch.chat.ui.OnConversationListChangedListener;
import de.gultsch.chat.ui.OnRosterFetchedListener; import de.gultsch.chat.ui.OnRosterFetchedListener;
import de.gultsch.chat.utils.UIHelper; import de.gultsch.chat.utils.UIHelper;
@ -19,20 +18,15 @@ import de.gultsch.chat.xmpp.IqPacket;
import de.gultsch.chat.xmpp.MessagePacket; import de.gultsch.chat.xmpp.MessagePacket;
import de.gultsch.chat.xmpp.OnIqPacketReceived; import de.gultsch.chat.xmpp.OnIqPacketReceived;
import de.gultsch.chat.xmpp.OnMessagePacketReceived; import de.gultsch.chat.xmpp.OnMessagePacketReceived;
import de.gultsch.chat.xmpp.OnStatusChanged;
import de.gultsch.chat.xmpp.XmppConnection; import de.gultsch.chat.xmpp.XmppConnection;
import android.R;
import android.R.dimen;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service; import android.app.Service;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.Resources;
import android.os.Binder; import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import android.os.PowerManager; import android.os.PowerManager;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.util.Log; import android.util.Log;
public class XmppConnectionService extends Service { public class XmppConnectionService extends Service {
@ -48,6 +42,7 @@ public class XmppConnectionService extends Service {
private Hashtable<Account, XmppConnection> connections = new Hashtable<Account, XmppConnection>(); private Hashtable<Account, XmppConnection> connections = new Hashtable<Account, XmppConnection>();
private OnConversationListChangedListener convChangedListener = null; private OnConversationListChangedListener convChangedListener = null;
private OnAccountListChangedListener accountChangedListener = null;
private final IBinder mBinder = new XmppConnectionBinder(); private final IBinder mBinder = new XmppConnectionBinder();
private OnMessagePacketReceived messageListener = new OnMessagePacketReceived() { private OnMessagePacketReceived messageListener = new OnMessagePacketReceived() {
@ -79,6 +74,16 @@ public class XmppConnectionService extends Service {
} }
} }
}; };
private OnStatusChanged statusListener = new OnStatusChanged() {
@Override
public void onStatusChanged(Account account) {
Log.d(LOGTAG,account.getJid()+" changed status to "+account.getStatus());
if (accountChangedListener != null) {
accountChangedListener.onAccountListChangedListener();
}
}
};
public class XmppConnectionBinder extends Binder { public class XmppConnectionBinder extends Binder {
public XmppConnectionService getService() { public XmppConnectionService getService() {
@ -88,15 +93,10 @@ public class XmppConnectionService extends Service {
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
for (Account account : accounts) { for (Account account : accounts) {
if (!connections.containsKey(account)) { if (!connections.containsKey(account)) {
XmppConnection connection = new XmppConnection(account, pm);
connection this.connections.put(account, this.createConnection(account));
.setOnMessagePacketReceivedListener(this.messageListener);
Thread thread = new Thread(connection);
thread.start();
this.connections.put(account, connection);
} }
} }
return START_STICKY; return START_STICKY;
@ -107,6 +107,17 @@ public class XmppConnectionService extends Service {
databaseBackend = DatabaseBackend.getInstance(getApplicationContext()); databaseBackend = DatabaseBackend.getInstance(getApplicationContext());
this.accounts = databaseBackend.getAccounts(); this.accounts = databaseBackend.getAccounts();
} }
public XmppConnection createConnection(Account account) {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
XmppConnection connection = new XmppConnection(account, pm);
connection
.setOnMessagePacketReceivedListener(this.messageListener);
connection.setOnStatusChangedListener(this.statusListener );
Thread thread = new Thread(connection);
thread.start();
return connection;
}
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
@ -114,68 +125,49 @@ public class XmppConnectionService extends Service {
} }
public void sendMessage(final Account account, final Message message) { public void sendMessage(final Account account, final Message message) {
new Thread() { Log.d(LOGTAG, "sending message for " + account.getJid() + " to: "
@Override + message.getCounterpart());
public void run() { databaseBackend.createMessage(message);
Log.d(LOGTAG, "sending message for " + account.getJid() MessagePacket packet = new MessagePacket();
+ " to: " + message.getCounterpart()); packet.setType(MessagePacket.TYPE_CHAT);
databaseBackend.createMessage(message); packet.setTo(message.getCounterpart());
MessagePacket packet = new MessagePacket(); packet.setFrom(account.getJid());
packet.setType(MessagePacket.TYPE_CHAT); packet.setBody(message.getBody());
packet.setTo(message.getCounterpart()); connections.get(account).sendMessagePacket(packet);
packet.setFrom(account.getJid()); message.setStatus(Message.STATUS_SEND);
packet.setBody(message.getBody()); databaseBackend.updateMessage(message);
try {
connections.get(account).sendMessagePacket(packet);
message.setStatus(Message.STATUS_SEND);
databaseBackend.updateMessage(message);
} catch (IOException e) {
Log.d(LOGTAG,
"io exception during send. message is in database. will try again later");
}
}
}.start();
} }
public void getRoster(final Account account, public void getRoster(final Account account,
final OnRosterFetchedListener listener) { final OnRosterFetchedListener listener) {
new Thread() { IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
@Override Element query = new Element("query");
public void run() { query.setAttribute("xmlns", "jabber:iq:roster");
IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET); query.setAttribute("ver", "");
Element query = new Element("query"); iqPacket.addChild(query);
query.setAttribute("xmlns", "jabber:iq:roster"); connections.get(account).sendIqPacket(iqPacket,
query.setAttribute("ver", ""); new OnIqPacketReceived() {
iqPacket.addChild(query);
try {
connections.get(account).sendIqPacket(iqPacket,
new OnIqPacketReceived() {
@Override @Override
public void onIqPacketReceived(Account account, public void onIqPacketReceived(Account account,
IqPacket packet) { IqPacket packet) {
Element roster = packet.findChild("query"); Element roster = packet.findChild("query");
List<Contact> contacts = new ArrayList<Contact>(); List<Contact> contacts = new ArrayList<Contact>();
for (Element item : roster.getChildren()) { for (Element item : roster.getChildren()) {
String name = item.getAttribute("name"); String name = item.getAttribute("name");
String jid = item.getAttribute("jid"); String jid = item.getAttribute("jid");
if (name == null) { if (name == null) {
name = jid.split("@")[0]; name = jid.split("@")[0];
} }
Contact contact = new Contact(account, Contact contact = new Contact(account, name, jid,
name, jid, null); null);
contacts.add(contact); contacts.add(contact);
} }
if (listener != null) { if (listener != null) {
listener.onRosterFetched(contacts); listener.onRosterFetched(contacts);
} }
} }
}); });
} catch (IOException e) {
Log.d(LOGTAG, "io error during roster fetch");
}
}
}.start();
} }
public void addConversation(Conversation conversation) { public void addConversation(Conversation conversation) {
@ -249,14 +241,32 @@ public class XmppConnectionService extends Service {
public void createAccount(Account account) { public void createAccount(Account account) {
databaseBackend.createAccount(account); databaseBackend.createAccount(account);
this.accounts.add(account);
this.connections.put(account, this.createConnection(account));
if (accountChangedListener!=null) accountChangedListener.onAccountListChangedListener();
} }
public void updateAccount(Account account) { public void updateAccount(Account account) {
databaseBackend.updateAccount(account); databaseBackend.updateAccount(account);
XmppConnection connection = this.connections.get(account);
if (connection != null) {
connection.disconnect();
this.connections.remove(account);
}
this.connections.put(account, this.createConnection(account));
if (accountChangedListener!=null) accountChangedListener.onAccountListChangedListener();
} }
public void deleteAccount(Account account) { public void deleteAccount(Account account) {
Log.d(LOGTAG,"called delete account");
if (this.connections.containsKey(account)) {
Log.d(LOGTAG,"found connection. disconnecting");
this.connections.get(account).disconnect();
this.connections.remove(account);
this.accounts.remove(account);
}
databaseBackend.deleteAccount(account); databaseBackend.deleteAccount(account);
if (accountChangedListener!=null) accountChangedListener.onAccountListChangedListener();
} }
public void setOnConversationListChangedListener( public void setOnConversationListChangedListener(
@ -267,4 +277,12 @@ public class XmppConnectionService extends Service {
public void removeOnConversationListChangedListener() { public void removeOnConversationListChangedListener() {
this.convChangedListener = null; this.convChangedListener = null;
} }
public void setOnAccountListChangedListener(OnAccountListChangedListener listener) {
this.accountChangedListener = listener;
}
public void removeOnAccountListChangedListener() {
this.accountChangedListener = null;
}
} }

View File

@ -6,7 +6,6 @@ import java.util.List;
import de.gultsch.chat.R; import de.gultsch.chat.R;
import de.gultsch.chat.entities.Account; import de.gultsch.chat.entities.Account;
import de.gultsch.chat.ui.EditAccount.EditAccountListener; import de.gultsch.chat.ui.EditAccount.EditAccountListener;
import android.app.ActionBar;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -25,28 +24,72 @@ import android.widget.TextView;
public class ManageAccountActivity extends XmppActivity { public class ManageAccountActivity extends XmppActivity {
protected List<Account> accountList = new ArrayList<Account>(); protected List<Account> accountList = new ArrayList<Account>();
protected ListView accountListView; protected ListView accountListView;
protected ArrayAdapter<Account> accountListViewAdapter; protected ArrayAdapter<Account> accountListViewAdapter;
protected OnAccountListChangedListener accountChanged = new OnAccountListChangedListener() {
@Override
public void onAccountListChangedListener() {
Log.d("xmppService", "ui on account list changed listener");
accountList.clear();
accountList.addAll(xmppConnectionService.getAccounts());
runOnUiThread(new Runnable() {
@Override
public void run() {
if (accountList.size() == 1) {
startActivity(new Intent(getApplicationContext(),
NewConversationActivity.class));
}
accountListViewAdapter.notifyDataSetChanged();
}
});
}
};
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.manage_accounts); setContentView(R.layout.manage_accounts);
accountListView = (ListView) findViewById(R.id.account_list); accountListView = (ListView) findViewById(R.id.account_list);
accountListViewAdapter = new ArrayAdapter<Account>(getApplicationContext(), R.layout.account_row, this.accountList) { accountListViewAdapter = new ArrayAdapter<Account>(
getApplicationContext(), R.layout.account_row, this.accountList) {
@Override @Override
public View getView(int position, View view, ViewGroup parent) { public View getView(int position, View view, ViewGroup parent) {
Account account = getItem(position);
if (view == null) { if (view == null) {
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = (View) inflater.inflate(R.layout.account_row, null); view = (View) inflater.inflate(R.layout.account_row, null);
} }
((TextView) view.findViewById(R.id.account_jid)).setText(getItem(position).getJid()); ((TextView) view.findViewById(R.id.account_jid))
.setText(account.getJid());
TextView statusView = (TextView) view
.findViewById(R.id.account_status);
switch (account.getStatus()) {
case Account.STATUS_ONLINE:
statusView.setText("online");
statusView.setTextColor(0xFF83b600);
break;
case Account.STATUS_OFFLINE:
statusView.setText("offline");
statusView.setTextColor(0xFFe92727);
break;
case Account.STATUS_UNAUTHORIZED:
statusView.setText("unauthorized");
statusView.setTextColor(0xFFe92727);
break;
case Account.STATUS_SERVER_NOT_FOUND:
statusView.setText("server not found");
statusView.setTextColor(0xFFe92727);
break;
default:
break;
}
return view; return view;
} }
}; };
@ -54,12 +97,12 @@ public class ManageAccountActivity extends XmppActivity {
accountListView.setOnItemClickListener(new OnItemClickListener() { accountListView.setOnItemClickListener(new OnItemClickListener() {
@Override @Override
public void onItemClick(AdapterView<?> arg0, View view, int position, public void onItemClick(AdapterView<?> arg0, View view,
long arg3) { int position, long arg3) {
EditAccount dialog = new EditAccount(); EditAccount dialog = new EditAccount();
dialog.setAccount(accountList.get(position)); dialog.setAccount(accountList.get(position));
dialog.setEditAccountListener(new EditAccountListener() { dialog.setEditAccountListener(new EditAccountListener() {
@Override @Override
public void onAccountEdited(Account account) { public void onAccountEdited(Account account) {
xmppConnectionService.updateAccount(account); xmppConnectionService.updateAccount(account);
@ -67,41 +110,27 @@ public class ManageAccountActivity extends XmppActivity {
@Override @Override
public void onAccountDelete(Account account) { public void onAccountDelete(Account account) {
Log.d("gultsch","deleting account:"+account.getJid());
xmppConnectionService.deleteAccount(account); xmppConnectionService.deleteAccount(account);
//dont bother finding the right account in the frontend list. just reload
accountList.clear();
accountList.addAll(xmppConnectionService.getAccounts());
accountListViewAdapter.notifyDataSetChanged();
} }
}); });
dialog.show(getFragmentManager(),"edit_account"); dialog.show(getFragmentManager(), "edit_account");
} }
}); });
} }
@Override @Override
public void onStart() { protected void onStop() {
super.onStart(); super.onStop();
if (xmppConnectionServiceBound) { if (xmppConnectionServiceBound) {
this.accountList.clear(); xmppConnectionService.removeOnAccountListChangedListener();
this.accountList.addAll(xmppConnectionService unbindService(mConnection);
.getAccounts()); xmppConnectionServiceBound = false;
accountListViewAdapter.notifyDataSetChanged();
if (this.accountList.size() == 0) {
getActionBar().setDisplayHomeAsUpEnabled(false);
}
} }
} }
@Override @Override
void onBackendConnected() { void onBackendConnected() {
Log.d("gultsch","called on backend connected"); xmppConnectionService.setOnAccountListChangedListener(accountChanged);
this.accountList.clear(); this.accountList.clear();
this.accountList.addAll(xmppConnectionService.getAccounts()); this.accountList.addAll(xmppConnectionService.getAccounts());
accountListViewAdapter.notifyDataSetChanged(); accountListViewAdapter.notifyDataSetChanged();
@ -110,14 +139,14 @@ public class ManageAccountActivity extends XmppActivity {
addAccount(); addAccount();
} }
} }
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present. // Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.manageaccounts, menu); getMenuInflater().inflate(R.menu.manageaccounts, menu);
return true; return true;
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
@ -137,23 +166,18 @@ public class ManageAccountActivity extends XmppActivity {
final Activity activity = this; final Activity activity = this;
EditAccount dialog = new EditAccount(); EditAccount dialog = new EditAccount();
dialog.setEditAccountListener(new EditAccountListener() { dialog.setEditAccountListener(new EditAccountListener() {
@Override @Override
public void onAccountEdited(Account account) { public void onAccountEdited(Account account) {
xmppConnectionService.createAccount(account); xmppConnectionService.createAccount(account);
accountList.add(account);
accountListViewAdapter.notifyDataSetChanged();
activity.getActionBar().setDisplayHomeAsUpEnabled(true); activity.getActionBar().setDisplayHomeAsUpEnabled(true);
if (accountList.size() == 1) {
activity.startActivity(new Intent(activity,NewConversationActivity.class));
}
} }
@Override @Override
public void onAccountDelete(Account account) { public void onAccountDelete(Account account) {
//this will never be called // this will never be called
} }
}); });
dialog.show(getFragmentManager(),"add_account"); dialog.show(getFragmentManager(), "add_account");
} }
} }

View File

@ -0,0 +1,5 @@
package de.gultsch.chat.ui;
public interface OnAccountListChangedListener {
public void onAccountListChangedListener();
}

View File

@ -3,12 +3,37 @@ package de.gultsch.chat.xml;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import android.util.Log; import android.util.Log;
public class TagWriter { public class TagWriter {
OutputStreamWriter writer; private OutputStreamWriter outputStream;
private LinkedBlockingQueue<String> writeQueue = new LinkedBlockingQueue<String>();
private Thread writer = new Thread() {
public boolean shouldStop = false;
@Override
public void run() {
while(!shouldStop) {
try {
String output = writeQueue.take();
outputStream.write(output);
outputStream.flush();
} catch (IOException e) {
Log.d("xmppService", "error writing to stream");
} catch (InterruptedException e) {
}
}
}
};
public TagWriter() { public TagWriter() {
@ -16,31 +41,29 @@ public class TagWriter {
public TagWriter(OutputStream out) { public TagWriter(OutputStream out) {
this.setOutputStream(out); this.setOutputStream(out);
writer.start();
} }
public void setOutputStream(OutputStream out) { public void setOutputStream(OutputStream out) {
this.writer = new OutputStreamWriter(out); this.outputStream = new OutputStreamWriter(out);
if (!writer.isAlive()) writer.start();
} }
public TagWriter beginDocument() throws IOException { public TagWriter beginDocument() {
writer.write("<?xml version='1.0'?>"); writeQueue.add("<?xml version='1.0'?>");
return this; return this;
} }
public TagWriter writeTag(Tag tag) throws IOException { public TagWriter writeTag(Tag tag) {
writer.write(tag.toString()); writeQueue.add(tag.toString());
return this; return this;
} }
public void flush() throws IOException { public void writeString(String string) {
writer.flush(); writeQueue.add(string);
} }
public void writeString(String string) throws IOException { public void writeElement(Element element) {
writer.write(string); writeQueue.add(element.toString());
}
public void writeElement(Element element) throws IOException {
writer.write(element.toString());
} }
} }

View File

@ -0,0 +1,7 @@
package de.gultsch.chat.xmpp;
import de.gultsch.chat.entities.Account;
public interface OnStatusChanged {
public void onStatusChanged(Account account);
}

View File

@ -39,7 +39,7 @@ public class XmppConnection implements Runnable {
private boolean isTlsEncrypted = false; private boolean isTlsEncrypted = false;
private boolean isAuthenticated = false; private boolean isAuthenticated = false;
//private boolean shouldUseTLS = false; //private boolean shouldUseTLS = false;
private boolean shouldReConnect = true; private boolean shouldConnect = true;
private boolean shouldBind = true; private boolean shouldBind = true;
private boolean shouldAuthenticate = true; private boolean shouldAuthenticate = true;
private Element streamFeatures; private Element streamFeatures;
@ -52,8 +52,7 @@ public class XmppConnection implements Runnable {
private OnPresencePacketReceived presenceListener = null; private OnPresencePacketReceived presenceListener = null;
private OnIqPacketReceived unregisteredIqListener = null; private OnIqPacketReceived unregisteredIqListener = null;
private OnMessagePacketReceived messageListener = null; private OnMessagePacketReceived messageListener = null;
private OnStatusChanged statusListener = null;
private String resource = null;
public XmppConnection(Account account, PowerManager pm) { public XmppConnection(Account account, PowerManager pm) {
this.account = account; this.account = account;
@ -66,7 +65,6 @@ public class XmppConnection implements Runnable {
protected void connect() { protected void connect() {
try { try {
socket = new Socket(account.getServer(), 5222); socket = new Socket(account.getServer(), 5222);
Log.d(LOGTAG, "starting new socket");
OutputStream out = socket.getOutputStream(); OutputStream out = socket.getOutputStream();
tagWriter.setOutputStream(out); tagWriter.setOutputStream(out);
InputStream in = socket.getInputStream(); InputStream in = socket.getInputStream();
@ -77,40 +75,54 @@ public class XmppConnection implements Runnable {
while ((nextTag = tagReader.readTag()) != null) { while ((nextTag = tagReader.readTag()) != null) {
if (nextTag.isStart("stream")) { if (nextTag.isStart("stream")) {
processStream(nextTag); processStream(nextTag);
break;
} else { } else {
Log.d(LOGTAG, "found unexpected tag: " + nextTag.getName()); Log.d(LOGTAG, "found unexpected tag: " + nextTag.getName());
return; return;
} }
} }
if (socket.isConnected()) {
socket.close();
}
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
Log.d(LOGTAG,account.getJid()+": error during connect. unknown host"); account.setStatus(Account.STATUS_SERVER_NOT_FOUND);
return; return;
} catch (IOException e) { } catch (IOException e) {
Log.d(LOGTAG, account.getJid()+": error during connect. io exception. falscher port?"); if (shouldConnect) {
return; Log.d(LOGTAG,account.getJid()+": connection lost");
account.setStatus(Account.STATUS_OFFLINE);
if (statusListener!=null) {
statusListener.onStatusChanged(account);
}
}
} catch (XmlPullParserException e) { } catch (XmlPullParserException e) {
Log.d(LOGTAG,"xml exception "+e.getMessage()); Log.d(LOGTAG,"xml exception "+e.getMessage());
return; return;
} }
} }
@Override @Override
public void run() { public void run() {
while(shouldReConnect) { shouldConnect = true;
while(shouldConnect) {
connect(); connect();
try { try {
Thread.sleep(30000); if (shouldConnect) {
Thread.sleep(30000);
}
} catch (InterruptedException e) { } catch (InterruptedException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
} }
} }
Log.d(LOGTAG,"end run");
} }
private void processStream(Tag currentTag) throws XmlPullParserException, private void processStream(Tag currentTag) throws XmlPullParserException,
IOException { IOException {
Tag nextTag; Tag nextTag = tagReader.readTag();
while (!(nextTag = tagReader.readTag()).isEnd("stream")) { while ((nextTag != null) && (!nextTag.isEnd("stream"))) {
if (nextTag.isStart("error")) { if (nextTag.isStart("error")) {
processStreamError(nextTag); processStreamError(nextTag);
} else if (nextTag.isStart("features")) { } else if (nextTag.isStart("features")) {
@ -124,6 +136,12 @@ public class XmppConnection implements Runnable {
tagReader.reset(); tagReader.reset();
sendStartStream(); sendStartStream();
processStream(tagReader.readTag()); processStream(tagReader.readTag());
break;
} else if(nextTag.isStart("failure")) {
Element failure = tagReader.readElement(nextTag);
Log.d(LOGTAG,"read failure element"+failure.toString());
account.setStatus(Account.STATUS_UNAUTHORIZED);
tagWriter.writeTag(Tag.end("stream"));
} else if (nextTag.isStart("iq")) { } else if (nextTag.isStart("iq")) {
processIq(nextTag); processIq(nextTag);
} else if (nextTag.isStart("message")) { } else if (nextTag.isStart("message")) {
@ -134,6 +152,13 @@ public class XmppConnection implements Runnable {
Log.d(LOGTAG, "found unexpected tag: " + nextTag.getName() Log.d(LOGTAG, "found unexpected tag: " + nextTag.getName()
+ " as child of " + currentTag.getName()); + " as child of " + currentTag.getName());
} }
nextTag = tagReader.readTag();
}
if (account.getStatus() == Account.STATUS_ONLINE) {
account.setStatus(Account.STATUS_OFFLINE);
if (statusListener!=null) {
statusListener.onStatusChanged(account);
}
} }
} }
@ -190,11 +215,11 @@ public class XmppConnection implements Runnable {
} }
} }
private void sendStartTLS() throws XmlPullParserException, IOException { private void sendStartTLS() {
Tag startTLS = Tag.empty("starttls"); Tag startTLS = Tag.empty("starttls");
startTLS.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-tls"); startTLS.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-tls");
Log.d(LOGTAG,account.getJid()+": sending starttls"); Log.d(LOGTAG,account.getJid()+": sending starttls");
tagWriter.writeTag(startTLS).flush(); tagWriter.writeTag(startTLS);
} }
private void switchOverToTls(Tag currentTag) throws XmlPullParserException, private void switchOverToTls(Tag currentTag) throws XmlPullParserException,
@ -213,6 +238,7 @@ public class XmppConnection implements Runnable {
isTlsEncrypted = true; isTlsEncrypted = true;
sendStartStream(); sendStartStream();
processStream(tagReader.readTag()); processStream(tagReader.readTag());
sslSocket.close();
} catch (IOException e) { } catch (IOException e) {
Log.d(LOGTAG, account.getJid()+": error on ssl '" + e.getMessage()+"'"); Log.d(LOGTAG, account.getJid()+": error on ssl '" + e.getMessage()+"'");
} }
@ -227,7 +253,6 @@ public class XmppConnection implements Runnable {
auth.setContent(saslString); auth.setContent(saslString);
Log.d(LOGTAG,account.getJid()+": sending sasl "+auth.toString()); Log.d(LOGTAG,account.getJid()+": sending sasl "+auth.toString());
tagWriter.writeElement(auth); tagWriter.writeElement(auth);
tagWriter.flush();
} }
private void processStreamFeatures(Tag currentTag) private void processStreamFeatures(Tag currentTag)
@ -249,12 +274,10 @@ public class XmppConnection implements Runnable {
startSession.addChild(session); startSession.addChild(session);
sendIqPacket(startSession, null); sendIqPacket(startSession, null);
tagWriter.writeElement(startSession); tagWriter.writeElement(startSession);
tagWriter.flush();
} }
Element presence = new Element("presence"); Element presence = new Element("presence");
tagWriter.writeElement(presence); tagWriter.writeElement(presence);
tagWriter.flush();
} }
} }
@ -266,8 +289,12 @@ public class XmppConnection implements Runnable {
this.sendIqPacket(iq, new OnIqPacketReceived() { this.sendIqPacket(iq, new OnIqPacketReceived() {
@Override @Override
public void onIqPacketReceived(Account account, IqPacket packet) { public void onIqPacketReceived(Account account, IqPacket packet) {
resource = packet.findChild("bind").findChild("jid").getContent().split("/")[1]; String resource = packet.findChild("bind").findChild("jid").getContent().split("/")[1];
Log.d(LOGTAG,account.getJid()+": new resource is "+resource); account.setResource(resource);
account.setStatus(Account.STATUS_ONLINE);
if (statusListener!=null) {
statusListener.onStatusChanged(account);
}
} }
}); });
} }
@ -276,7 +303,7 @@ public class XmppConnection implements Runnable {
Log.d(LOGTAG, "processStreamError"); Log.d(LOGTAG, "processStreamError");
} }
private void sendStartStream() throws IOException { private void sendStartStream() {
Tag stream = Tag.start("stream"); Tag stream = Tag.start("stream");
stream.setAttribute("from", account.getJid()); stream.setAttribute("from", account.getJid());
stream.setAttribute("to", account.getServer()); stream.setAttribute("to", account.getServer());
@ -284,32 +311,29 @@ public class XmppConnection implements Runnable {
stream.setAttribute("xml:lang", "en"); stream.setAttribute("xml:lang", "en");
stream.setAttribute("xmlns", "jabber:client"); stream.setAttribute("xmlns", "jabber:client");
stream.setAttribute("xmlns:stream", "http://etherx.jabber.org/streams"); stream.setAttribute("xmlns:stream", "http://etherx.jabber.org/streams");
tagWriter.writeTag(stream).flush(); tagWriter.writeTag(stream);
} }
private String nextRandomId() { private String nextRandomId() {
return new BigInteger(50, random).toString(32); return new BigInteger(50, random).toString(32);
} }
public void sendIqPacket(IqPacket packet, OnIqPacketReceived callback) throws IOException { public void sendIqPacket(IqPacket packet, OnIqPacketReceived callback) {
String id = nextRandomId(); String id = nextRandomId();
packet.setAttribute("id",id); packet.setAttribute("id",id);
tagWriter.writeElement(packet); tagWriter.writeElement(packet);
if (callback != null) { if (callback != null) {
iqPacketCallbacks.put(id, callback); iqPacketCallbacks.put(id, callback);
} }
tagWriter.flush();
Log.d(LOGTAG,account.getJid()+": sending: "+packet.toString()); Log.d(LOGTAG,account.getJid()+": sending: "+packet.toString());
} }
public void sendMessagePacket(MessagePacket packet) throws IOException { public void sendMessagePacket(MessagePacket packet){
tagWriter.writeElement(packet); tagWriter.writeElement(packet);
tagWriter.flush();
} }
public void sendPresencePacket(PresencePacket packet) throws IOException { public void sendPresencePacket(PresencePacket packet) {
tagWriter.writeElement(packet); tagWriter.writeElement(packet);
tagWriter.flush();
} }
public void setOnMessagePacketReceivedListener(OnMessagePacketReceived listener) { public void setOnMessagePacketReceivedListener(OnMessagePacketReceived listener) {
@ -323,4 +347,13 @@ public class XmppConnection implements Runnable {
public void setOnPresencePacketReceivedListener(OnPresencePacketReceived listener) { public void setOnPresencePacketReceivedListener(OnPresencePacketReceived listener) {
this.presenceListener = listener; this.presenceListener = listener;
} }
public void setOnStatusChangedListener(OnStatusChanged listener) {
this.statusListener = listener;
}
public void disconnect() {
shouldConnect = false;
tagWriter.writeTag(Tag.end("stream"));
}
} }