Merge branch 'watchdog'

Daniel Gultsch created

Change summary

AndroidManifest.xml                                            |  37 
src/eu/siacs/conversations/entities/Account.java               |   9 
src/eu/siacs/conversations/services/EventReceiver.java         |  19 
src/eu/siacs/conversations/services/XmppConnectionService.java |  54 +
src/eu/siacs/conversations/ui/ManageAccountActivity.java       | 154 ++-
src/eu/siacs/conversations/ui/XmppActivity.java                |   2 
src/eu/siacs/conversations/utils/DNSHelper.java                |   7 
src/eu/siacs/conversations/xmpp/XmppConnection.java            |  42 
8 files changed, 201 insertions(+), 123 deletions(-)

Detailed changes

AndroidManifest.xml 🔗

@@ -9,28 +9,41 @@
         android:targetSdkVersion="19" />
 
     <uses-permission android:name="android.permission.READ_CONTACTS" />
-    <uses-permission android:name="android.permission.READ_PROFILE"/>
+    <uses-permission android:name="android.permission.READ_PROFILE" />
     <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.WAKE_LOCK"/>
-    
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+
     <application
         android:allowBackup="true"
         android:icon="@drawable/ic_launcher"
         android:label="@string/app_name"
         android:theme="@android:style/Theme.Holo.Light" >
-        <service android:name="eu.siacs.conversations.services.XmppConnectionService"/>
+        <service android:name="eu.siacs.conversations.services.XmppConnectionService" />
+
+        <receiver android:name="eu.siacs.conversations.services.EventReceiver" >
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED" />
+                <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
+            </intent-filter>
+        </receiver>
+
         <activity
             android:name="eu.siacs.conversations.ui.ConversationActivity"
+            android:configChanges="orientation|screenSize"
             android:label="Conversations"
-            android:windowSoftInputMode="stateHidden"
-            android:configChanges="orientation|screenSize">
+            android:windowSoftInputMode="stateHidden" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
+
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
-             <intent-filter>
+            <intent-filter>
                 <action android:name="android.intent.action.SENDTO" />
+
                 <category android:name="android.intent.category.DEFAULT" />
+
                 <data android:scheme="imto" />
                 <data android:host="jabber" />
             </intent-filter>
@@ -45,25 +58,25 @@
             android:label="Manage Accounts"
             android:parentActivityName="eu.siacs.conversations.ui.ConversationActivity" >
         </activity>
-         <activity
+        <activity
             android:name="eu.siacs.conversations.ui.MucDetailsActivity"
             android:label="Conference Details"
-            android:windowSoftInputMode="stateHidden">
+            android:windowSoftInputMode="stateHidden" >
         </activity>
         <activity
             android:name="eu.siacs.conversations.ui.ContactDetailsActivity"
             android:label="Contact Details"
-            android:windowSoftInputMode="stateHidden">
+            android:windowSoftInputMode="stateHidden" >
         </activity>
         <activity
             android:name="eu.siacs.conversations.ui.NewConversationActivity"
             android:label="@string/title_activity_new_conversation"
             android:parentActivityName="eu.siacs.conversations.ui.ConversationActivity"
-            android:windowSoftInputMode="stateHidden">
+            android:windowSoftInputMode="stateHidden" >
             <meta-data
                 android:name="android.support.PARENT_ACTIVITY"
                 android:value="de.gultsch.chat.ui.ConversationActivity" />
         </activity>
     </application>
 
-</manifest>
+</manifest>

src/eu/siacs/conversations/entities/Account.java 🔗

@@ -32,11 +32,12 @@ public class Account  extends AbstractEntity{
 	public static final int OPTION_USETLS = 0;
 	public static final int OPTION_DISABLED = 1;
 	
-	public static final int STATUS_DISABLED = -1;
-	public static final int STATUS_OFFLINE = 0;
+	public static final int STATUS_CONNECTING = 0;
+	public static final int STATUS_DISABLED = -2;
+	public static final int STATUS_OFFLINE = -1;
 	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_NO_INTERNET = 3;
 	public static final int STATUS_TLS_ERROR = 4;
 	public static final int STATUS_SERVER_NOT_FOUND = 5;
 	
@@ -46,7 +47,7 @@ public class Account  extends AbstractEntity{
 	protected int options = 0;
 	protected String rosterVersion;
 	protected String resource;
-	protected int status = 0;
+	protected int status = -1;
 	protected JSONObject keys = new JSONObject();
 	
 	protected boolean online = false;

src/eu/siacs/conversations/services/EventReceiver.java 🔗

@@ -0,0 +1,19 @@
+package eu.siacs.conversations.services;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+public class EventReceiver extends BroadcastReceiver {
+	@Override
+	public void onReceive(Context context, Intent intent) {
+		Intent mIntentForService = new Intent(context,
+				XmppConnectionService.class);
+		if ((intent.getAction() != null)
+				&& (intent.getAction()
+						.equals("android.intent.action.BOOT_COMPLETED"))) {
+
+		}
+		context.startService(mIntentForService);
+	}
+
+}

src/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -5,6 +5,7 @@ import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.Random;
 
 import org.json.JSONException;
 import org.openintents.openpgp.util.OpenPgpApi;
@@ -42,17 +43,22 @@ import eu.siacs.conversations.xmpp.OnPresencePacketReceived;
 import eu.siacs.conversations.xmpp.OnStatusChanged;
 import eu.siacs.conversations.xmpp.PresencePacket;
 import eu.siacs.conversations.xmpp.XmppConnection;
+import android.app.AlarmManager;
 import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.database.ContentObserver;
 import android.database.DatabaseUtils;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.PowerManager;
+import android.os.SystemClock;
 import android.preference.PreferenceManager;
 import android.provider.ContactsContract;
 import android.util.Log;
@@ -70,6 +76,8 @@ public class XmppConnectionService extends Service {
 
 	public OnConversationListChangedListener convChangedListener = null;
 	private OnAccountListChangedListener accountChangedListener = null;
+	
+	private Random mRandom = new Random(System.currentTimeMillis());
 
 	private ContentObserver contactObserver = new ContentObserver(null) {
 		@Override
@@ -183,6 +191,14 @@ public class XmppConnectionService extends Service {
 						//
 					}
 				}
+			} else if (account.getStatus() == Account.STATUS_OFFLINE) {
+				Log.d(LOGTAG,"onStatusChanged offline");
+				databaseBackend.clearPresences(account);
+				if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+					int timeToReconnect = mRandom.nextInt(50)+10;
+					scheduleWakeupCall(timeToReconnect);
+				}
+
 			}
 		}
 	};
@@ -364,11 +380,30 @@ public class XmppConnectionService extends Service {
 
 	@Override
 	public int onStartCommand(Intent intent, int flags, int startId) {
+		ConnectivityManager cm = (ConnectivityManager) getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+
+		NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
+		boolean isConnected = activeNetwork != null
+				&& activeNetwork.isConnected();
 		for (Account account : accounts) {
-			if (account.getXmppConnection() == null) {
-				if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+			if (!isConnected) {
+				account.setStatus(Account.STATUS_NO_INTERNET);
+			} else {
+				if (account.getStatus() == Account.STATUS_NO_INTERNET) {
+					account.setStatus(Account.STATUS_OFFLINE);
+				}
+			}
+			if (accountChangedListener!=null) {
+				accountChangedListener.onAccountListChangedListener();
+			}
+			if ((!account.isOptionSet(Account.OPTION_DISABLED))&&(isConnected)) {
+				if (account.getXmppConnection() == null) {
 					account.setXmppConnection(this.createConnection(account));
 				}
+				if (account.getStatus()==Account.STATUS_OFFLINE) {
+					Thread thread = new Thread(account.getXmppConnection());
+					thread.start();
+				}
 			}
 		}
 		return START_STICKY;
@@ -384,6 +419,8 @@ public class XmppConnectionService extends Service {
 		this.pgpServiceConnection = new OpenPgpServiceConnection(
 				getApplicationContext(), "org.sufficientlysecure.keychain");
 		this.pgpServiceConnection.bindToService();
+		
+		
 	}
 
 	@Override
@@ -395,6 +432,17 @@ public class XmppConnectionService extends Service {
 			}
 		}
 	}
+	
+	protected void scheduleWakeupCall(int seconds) {
+		Context context = getApplicationContext();
+		AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+		Intent intent = new Intent(context, EventReceiver.class);
+		PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
+		alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+		        SystemClock.elapsedRealtime() +
+		        seconds * 1000, alarmIntent);
+		Log.d(LOGTAG,"wake up call scheduled in "+seconds+" seconds");
+	}
 
 	public XmppConnection createConnection(Account account) {
 		PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
@@ -404,8 +452,6 @@ public class XmppConnectionService extends Service {
 		connection.setOnPresencePacketReceivedListener(this.presenceListener);
 		connection
 				.setOnUnregisteredIqPacketReceivedListener(this.unknownIqListener);
-		Thread thread = new Thread(connection);
-		thread.start();
 		return connection;
 	}
 

src/eu/siacs/conversations/ui/ManageAccountActivity.java 🔗

@@ -32,7 +32,7 @@ import android.widget.ArrayAdapter;
 import android.widget.ListView;
 import android.widget.TextView;
 
-public class ManageAccountActivity extends XmppActivity implements ActionMode.Callback {
+public class ManageAccountActivity extends XmppActivity {
 
 	public static final int REQUEST_ANNOUNCE_PGP = 0x73731;
 	
@@ -54,10 +54,6 @@ public class ManageAccountActivity extends XmppActivity implements ActionMode.Ca
 
 				@Override
 				public void run() {
-					if (accountList.size() == 1) {
-						startActivity(new Intent(getApplicationContext(),
-								NewConversationActivity.class));
-					}
 					accountListViewAdapter.notifyDataSetChanged();
 				}
 			});
@@ -94,6 +90,10 @@ public class ManageAccountActivity extends XmppActivity implements ActionMode.Ca
 					statusView.setText("online");
 					statusView.setTextColor(0xFF83b600);
 					break;
+				case Account.STATUS_CONNECTING:
+					statusView.setText("connecting\u2026");
+					statusView.setTextColor(0xFF1da9da);
+					break;
 				case Account.STATUS_OFFLINE:
 					statusView.setText("offline");
 					statusView.setTextColor(0xFFe92727);
@@ -106,6 +106,10 @@ public class ManageAccountActivity extends XmppActivity implements ActionMode.Ca
 					statusView.setText("server not found");
 					statusView.setTextColor(0xFFe92727);
 					break;
+				case Account.STATUS_NO_INTERNET:
+					statusView.setText("no internet");
+					statusView.setTextColor(0xFFe92727);
+					break;
 				default:
 					break;
 				}
@@ -113,7 +117,7 @@ public class ManageAccountActivity extends XmppActivity implements ActionMode.Ca
 				return view;
 			}
 		};
-		final Activity activity = this;
+		final XmppActivity activity = this;
 		accountListView.setAdapter(this.accountListViewAdapter);
 		accountListView.setOnItemClickListener(new OnItemClickListener() {
 
@@ -146,7 +150,76 @@ public class ManageAccountActivity extends XmppActivity implements ActionMode.Ca
 					accountListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
 					accountListView.setItemChecked(position,true);
 					selectedAccountForActionMode = accountList.get(position);
-					actionMode = activity.startActionMode((Callback) activity);
+					actionMode = activity.startActionMode((new ActionMode.Callback() {
+						
+						@Override
+						public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+							if (selectedAccountForActionMode.isOptionSet(Account.OPTION_DISABLED)) {
+					        	menu.findItem(R.id.account_enable).setVisible(true);
+					        	menu.findItem(R.id.account_disable).setVisible(false);
+					        } else {
+					        	menu.findItem(R.id.account_disable).setVisible(true);
+					        	menu.findItem(R.id.account_enable).setVisible(false);
+					        }
+							return true;
+						}
+						
+						@Override
+						public void onDestroyActionMode(ActionMode mode) {
+							// TODO Auto-generated method stub
+							
+						}
+						
+						@Override
+						public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+							MenuInflater inflater = mode.getMenuInflater();
+					        inflater.inflate(R.menu.manageaccounts_context, menu);
+							return true;
+						}
+						
+						@Override
+						public boolean onActionItemClicked(final ActionMode mode, MenuItem item) {
+							if (item.getItemId()==R.id.account_disable) {
+								selectedAccountForActionMode.setOption(Account.OPTION_DISABLED, true);
+								xmppConnectionService.updateAccount(selectedAccountForActionMode);
+								mode.finish();
+							} else if (item.getItemId()==R.id.account_enable) {
+								selectedAccountForActionMode.setOption(Account.OPTION_DISABLED, false);
+								xmppConnectionService.updateAccount(selectedAccountForActionMode);
+								mode.finish();
+							} else if (item.getItemId()==R.id.account_delete) {
+								AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+								builder.setTitle("Are you sure?");
+								builder.setIconAttribute(android.R.attr.alertDialogIcon);
+								builder.setMessage("If you delete your account your entire conversation history will be lost");
+								builder.setPositiveButton("Delete", new OnClickListener() {
+									
+									@Override
+									public void onClick(DialogInterface dialog, int which) {
+										xmppConnectionService.deleteAccount(selectedAccountForActionMode);
+										selectedAccountForActionMode = null;
+										mode.finish();
+									}
+								});
+								builder.setNegativeButton("Cancel",null);
+								builder.create().show();
+							} else if (item.getItemId()==R.id.announce_pgp) {
+								if (activity.hasPgp()) {
+									mode.finish();
+									try {
+										xmppConnectionService.generatePgpAnnouncement(selectedAccountForActionMode);
+									} catch (PgpEngine.UserInputRequiredException e) {
+										try {
+											startIntentSenderForResult(e.getPendingIntent().getIntentSender(), REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
+										} catch (SendIntentException e1) {
+											Log.d("gultsch","sending intent failed");
+										}
+									}
+								}
+							}
+							return true;
+						}
+					}));
 					return true;
 				} else {
 					return false;
@@ -210,73 +283,6 @@ public class ManageAccountActivity extends XmppActivity implements ActionMode.Ca
 		dialog.show(getFragmentManager(), "add_account");
 	}
 
-	@Override
-	public boolean onActionItemClicked(final ActionMode mode, MenuItem item) {
-		if (item.getItemId()==R.id.account_disable) {
-			selectedAccountForActionMode.setOption(Account.OPTION_DISABLED, true);
-			xmppConnectionService.updateAccount(selectedAccountForActionMode);
-			mode.finish();
-		} else if (item.getItemId()==R.id.account_enable) {
-			selectedAccountForActionMode.setOption(Account.OPTION_DISABLED, false);
-			xmppConnectionService.updateAccount(selectedAccountForActionMode);
-			mode.finish();
-		} else if (item.getItemId()==R.id.account_delete) {
-			AlertDialog.Builder builder = new AlertDialog.Builder(this);
-			builder.setTitle("Are you sure?");
-			builder.setIconAttribute(android.R.attr.alertDialogIcon);
-			builder.setMessage("If you delete your account your entire conversation history will be lost");
-			builder.setPositiveButton("Delete", new OnClickListener() {
-				
-				@Override
-				public void onClick(DialogInterface dialog, int which) {
-					xmppConnectionService.deleteAccount(selectedAccountForActionMode);
-					selectedAccountForActionMode = null;
-					mode.finish();
-				}
-			});
-			builder.setNegativeButton("Cancel",null);
-			builder.create().show();
-		} else if (item.getItemId()==R.id.announce_pgp) {
-			if (this.hasPgp()) {
-				mode.finish();
-				try {
-					xmppConnectionService.generatePgpAnnouncement(selectedAccountForActionMode);
-				} catch (PgpEngine.UserInputRequiredException e) {
-					try {
-						startIntentSenderForResult(e.getPendingIntent().getIntentSender(), REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
-					} catch (SendIntentException e1) {
-						Log.d("gultsch","sending intent failed");
-					}
-				}
-			}
-		}
-		return true;
-	}
-
-	@Override
-	public boolean onCreateActionMode(ActionMode mode, Menu menu) {
-		MenuInflater inflater = mode.getMenuInflater();
-        inflater.inflate(R.menu.manageaccounts_context, menu);
-		return true;
-	}
-
-	@Override
-	public void onDestroyActionMode(ActionMode mode) {
-		// TODO Auto-generated method stub
-		
-	}
-
-	@Override
-	public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
-		if (selectedAccountForActionMode.isOptionSet(Account.OPTION_DISABLED)) {
-        	menu.findItem(R.id.account_enable).setVisible(true);
-        	menu.findItem(R.id.account_disable).setVisible(false);
-        } else {
-        	menu.findItem(R.id.account_disable).setVisible(true);
-        	menu.findItem(R.id.account_enable).setVisible(false);
-        }
-		return true;
-	}
 	
 	@Override
 	public void onActionModeStarted(ActionMode mode) {

src/eu/siacs/conversations/ui/XmppActivity.java 🔗

@@ -37,9 +37,9 @@ public abstract class XmppActivity extends Activity {
 	
 	@Override
 	protected void onStart() {
-		startService(new Intent(this, XmppConnectionService.class));
 		super.onStart();
 		if (!xmppConnectionServiceBound) {
+			startService(new Intent(this, XmppConnectionService.class));
 			Intent intent = new Intent(this, XmppConnectionService.class);
 			bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
 		}

src/eu/siacs/conversations/utils/DNSHelper.java 🔗

@@ -8,12 +8,10 @@ import java.net.InetAddress;
 import java.util.Random;
 
 import android.os.Bundle;
-import android.util.Log;
 
 public class DNSHelper {
-	public static Bundle getSRVRecord(String host) {
+	public static Bundle getSRVRecord(String host) throws IOException {
 		Bundle namePort = new Bundle();
-		try {
 			String[] hostParts = host.split("\\.");
 			byte[] transId = new byte[2];
 			Random random = new Random();
@@ -70,9 +68,6 @@ public class DNSHelper {
 			}
 			builder.replace(0, 1, "");
 			namePort.putString("name",builder.toString());
-		} catch (IOException e) {
-			Log.d("xmppService","gut" + e.getMessage());
-		}
 		return namePort;
 	}
 

src/eu/siacs/conversations/xmpp/XmppConnection.java 🔗

@@ -66,9 +66,17 @@ public class XmppConnection implements Runnable {
 		tagReader = new XmlReader(wakeLock);
 		tagWriter = new TagWriter();
 	}
+	
+	protected void changeStatus(int nextStatus) {
+		account.setStatus(nextStatus);
+		if (statusListener != null) {
+			statusListener.onStatusChanged(account);
+		}
+	}
 
 	protected void connect() {
 		try {
+			this.changeStatus(Account.STATUS_CONNECTING);
 			Bundle namePort = DNSHelper.getSRVRecord(account.getServer());
 			String srvRecordServer = namePort.getString("name");
 			int srvRecordPort = namePort.getInt("port");
@@ -99,22 +107,23 @@ public class XmppConnection implements Runnable {
 				socket.close();
 			}
 		} catch (UnknownHostException e) {
-			account.setStatus(Account.STATUS_SERVER_NOT_FOUND);
-			if (statusListener != null) {
-				statusListener.onStatusChanged(account);
+			this.changeStatus(Account.STATUS_SERVER_NOT_FOUND);
+			if (wakeLock.isHeld()) {
+				wakeLock.release();
 			}
 			return;
 		} catch (IOException e) {
-			Log.d(LOGTAG, "bla " + e.getMessage());
-			if (shouldConnect) {
-				Log.d(LOGTAG, account.getJid() + ": connection lost");
-				account.setStatus(Account.STATUS_OFFLINE);
-				if (statusListener != null) {
-					statusListener.onStatusChanged(account);
-				}
+			this.changeStatus(Account.STATUS_OFFLINE);
+			if (wakeLock.isHeld()) {
+				wakeLock.release();
 			}
+			return;
 		} catch (XmlPullParserException e) {
+			this.changeStatus(Account.STATUS_OFFLINE);
 			Log.d(LOGTAG, "xml exception " + e.getMessage());
+			if (wakeLock.isHeld()) {
+				wakeLock.release();
+			}
 			return;
 		}
 
@@ -122,18 +131,7 @@ public class XmppConnection implements Runnable {
 
 	@Override
 	public void run() {
-		shouldConnect = true;
-		while (shouldConnect) {
-			connect();
-			try {
-				if (shouldConnect) {
-					Thread.sleep(30000);
-				}
-			} catch (InterruptedException e) {
-				// TODO Auto-generated catch block
-				e.printStackTrace();
-			}
-		}
+		connect();
 		Log.d(LOGTAG, "end run");
 	}