The current auto-reconnection implementation will only try reconnecting
once, immediately after the server is disconnected.  This will of course
almost always fail if the network is down or otherwise unavailable, so
as it stands, enabling auto-reconnect isn't particularly useful.

This patch implements multiple retries for auto-reconnect, with the
frequency of retries controlled by a preference.  The Android alarm
infrastructure is used to schedule reconnection attempts; if the phone
misses a scheduled attempt while it's asleep, the reconnection will be
attempted the next time the phone wakes up.
---
I'm unclear as to whether it'd be desirable to make sure the phone wakes
up to try to reconnect to the server -- it would seem to be important if
the user asks for a long reconnect interval, but detrimental to battery
life if the user chooses a short reconnect interval.  In any event, if
we decide we do want to wake the phone up, replace ELAPSED_REALTIME with
ELAPSED_REALTIME_WAKEUP when setting the alarm.

 application/res/values/arrays.xml                  |   16 ++++
 application/res/values/settings.xml                |    3 +
 application/res/values/strings.xml                 |    3 +
 application/res/xml/preferences.xml                |    9 ++
 application/src/org/yaaic/irc/IRCService.java      |   83 ++++++++++++++++++-
 application/src/org/yaaic/model/Broadcast.java     |    1 +
 application/src/org/yaaic/model/Settings.java      |   13 +++
 .../src/org/yaaic/receiver/ReconnectReceiver.java  |   64 +++++++++++++++
 8 files changed, 187 insertions(+), 5 deletions(-)
 create mode 100644 application/src/org/yaaic/receiver/ReconnectReceiver.java

diff --git a/application/res/values/arrays.xml 
b/application/res/values/arrays.xml
index 1b81fcd..21db3e3 100644
--- a/application/res/values/arrays.xml
+++ b/application/res/values/arrays.xml
@@ -30,4 +30,20 @@
         <item>19</item>
         <item>20</item>
     </string-array>
+    <string-array name="reconnect_interval_labels">
+        <item>1 minute</item>
+        <item>5 minutes</item>
+        <item>10 minutes</item>
+        <item>15 minutes</item>
+        <item>20 minutes</item>
+        <item>30 minutes</item>
+    </string-array>
+    <string-array name="reconnect_interval_values">
+        <item>1</item>
+        <item>5</item>
+        <item>10</item>
+        <item>15</item>
+        <item>20</item>
+        <item>30</item>
+    </string-array>
 </resources>
diff --git a/application/res/values/settings.xml 
b/application/res/values/settings.xml
index 4bc3bb5..3c8ce33 100644
--- a/application/res/values/settings.xml
+++ b/application/res/values/settings.xml
@@ -18,6 +18,9 @@
     <string name="key_reconnect">reconnect</string>
     <string name="default_reconnect">false</string>
     
+    <string name="key_reconnect_interval">reconnect_interval</string>
+    <string name="default_reconnect_interval">5</string>
+    
     <string name="key_quitmessage">quitmessage</string>
     <string name="default_quitmessage">Yaaic - Yet another Android IRC client 
- http://www.yaaic.org</string>
     
diff --git a/application/res/values/strings.xml 
b/application/res/values/strings.xml
index 080c39a..74c3bb8 100644
--- a/application/res/values/strings.xml
+++ b/application/res/values/strings.xml
@@ -176,6 +176,9 @@
     <string name="settings_connection">Connection</string>
     <string name="settings_reconnect_title">Reconnect</string>
     <string name="settings_reconnect_desc">Automatically reconnect on 
disconnect</string>
+    <string name="settings_reconnect_interval_title">Reconnect 
interval</string>
+    <string name="settings_reconnect_interval_desc">Number of minutes between 
reconnection tries</string>
+    <string name="settings_reconnect_interval_dialog_title">Reconnect 
interval</string>
 
     <string name="settings_chat">Chat</string>
     <string name="settings_icons_title">Show icons</string>
diff --git a/application/res/xml/preferences.xml 
b/application/res/xml/preferences.xml
index 7101259..4adaf09 100644
--- a/application/res/xml/preferences.xml
+++ b/application/res/xml/preferences.xml
@@ -28,6 +28,15 @@ along with Yaaic.  If not, see 
<http://www.gnu.org/licenses/>.
             android:summary="@string/settings_reconnect_desc"
             android:key="@string/key_reconnect"
             android:defaultValue="@string/default_reconnect" />
+        <ListPreference
+            android:title="@string/settings_reconnect_interval_title"
+            android:summary="@string/settings_reconnect_interval_desc"
+            
android:dialogTitle="@string/settings_reconnect_interval_dialog_title"
+            android:dependency="@string/key_reconnect"
+            android:entries="@array/reconnect_interval_labels"
+            android:entryValues="@array/reconnect_interval_values"
+            android:key="@string/key_reconnect_interval"
+            android:defaultValue="@string/default_reconnect_interval" />
     </PreferenceCategory>
     <PreferenceCategory
         android:title="@string/settings_chat">
diff --git a/application/src/org/yaaic/irc/IRCService.java 
b/application/src/org/yaaic/irc/IRCService.java
index ea0892c..e0c20ce 100644
--- a/application/src/org/yaaic/irc/IRCService.java
+++ b/application/src/org/yaaic/irc/IRCService.java
@@ -39,12 +39,16 @@ import org.yaaic.model.Server;
 import org.yaaic.model.ServerInfo;
 import org.yaaic.model.Settings;
 import org.yaaic.model.Status;
+import org.yaaic.receiver.ReconnectReceiver;
 
+import android.app.AlarmManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
 import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.SystemClock;
 
 /**
  * The background service for managing the irc connections
@@ -81,6 +85,10 @@ public class IRCService extends Service
     private Notification notification;
     private Settings settings;
 
+    private HashMap<Integer, PendingIntent> alarmIntents;
+    private HashMap<Integer, ReconnectReceiver> alarmReceivers;
+    private Object alarmIntentsLock;
+
     /**
      * Create new service
      */
@@ -92,6 +100,9 @@ public class IRCService extends Service
         this.binder = new IRCBinder(this);
         this.connectedServerTitles = new ArrayList<String>();
         this.mentions = new LinkedHashMap<String, Conversation>();
+        this.alarmIntents = new HashMap<Integer, PendingIntent>();
+        this.alarmReceivers = new HashMap<Integer, ReconnectReceiver>();
+        this.alarmIntentsLock = new Object();
     }
 
     /**
@@ -383,11 +394,31 @@ public class IRCService extends Service
      */
     public void connect(final Server server)
     {
-        new Thread() {
+        final int serverId = server.getId();
+        final int reconnectInterval = settings.getReconnectInterval()*60000;
+        final IRCService service = this;
+
+        if (settings.isReconnectEnabled()) {
+            server.setMayReconnect(true);
+        }
+
+        new Thread("Connect thread for " + server.getTitle()) {
             @Override
             public void run() {
+                synchronized(alarmIntentsLock) {
+                    alarmIntents.remove(serverId);
+                    ReconnectReceiver lastReceiver = 
alarmReceivers.remove(serverId);
+                    if (lastReceiver != null) {
+                        unregisterReceiver(lastReceiver);
+                    }
+                }
+
+                if (settings.isReconnectEnabled() && !server.mayReconnect()) {
+                    return;
+                }
+
                 try {
-                    IRCConnection connection = getConnection(server.getId());
+                    IRCConnection connection = getConnection(serverId);
 
                     connection.setNickname(server.getIdentity().getNickname());
                     connection.setAliases(server.getIdentity().getAliases());
@@ -415,19 +446,33 @@ public class IRCService extends Service
                 catch (Exception e) {
                     server.setStatus(Status.DISCONNECTED);
 
-                    Intent sIntent = 
Broadcast.createServerIntent(Broadcast.SERVER_UPDATE, server.getId());
+                    Intent sIntent = 
Broadcast.createServerIntent(Broadcast.SERVER_UPDATE, serverId);
                     sendBroadcast(sIntent);
 
-                    IRCConnection connection = getConnection(server.getId());
+                    IRCConnection connection = getConnection(serverId);
 
                     Message message;
 
                     if (e instanceof NickAlreadyInUseException) {
                         message = new 
Message(getString(R.string.nickname_in_use, connection.getNick()));
+                        server.setMayReconnect(false);
                     } else if (e instanceof IrcException) {
                         message = new 
Message(getString(R.string.irc_login_error, server.getHost(), 
server.getPort()));
+                        server.setMayReconnect(false);
                     } else {
                         message = new 
Message(getString(R.string.could_not_connect, server.getHost(), 
server.getPort()));
+                        if (settings.isReconnectEnabled()) {
+                            Intent rIntent = new 
Intent(Broadcast.SERVER_RECONNECT + serverId);
+                            PendingIntent pendingRIntent = 
PendingIntent.getBroadcast(service, 0, rIntent, 0);
+                            AlarmManager am = (AlarmManager) 
getSystemService(ALARM_SERVICE);
+                            ReconnectReceiver receiver = new 
ReconnectReceiver(service, server);
+                            synchronized(alarmIntentsLock) {
+                                alarmReceivers.put(serverId, receiver);
+                                registerReceiver(receiver, new 
IntentFilter(Broadcast.SERVER_RECONNECT + serverId));
+                                am.set(AlarmManager.ELAPSED_REALTIME, 
SystemClock.elapsedRealtime() + reconnectInterval, pendingRIntent);
+                                alarmIntents.put(serverId, pendingRIntent);
+                            }
+                        }
                     }
 
                     message.setColor(Message.COLOR_RED);
@@ -436,7 +481,7 @@ public class IRCService extends Service
 
                     Intent cIntent = Broadcast.createConversationIntent(
                         Broadcast.CONVERSATION_MESSAGE,
-                        server.getId(),
+                        serverId,
                         ServerInfo.DEFAULT_NAME
                     );
                     sendBroadcast(cIntent);
@@ -494,6 +539,20 @@ public class IRCService extends Service
                     }
                     connections.remove(serverId);
                 }
+
+                synchronized(alarmIntentsLock) {
+                    PendingIntent pendingRIntent = alarmIntents.get(serverId);
+                    if (pendingRIntent != null) {
+                        AlarmManager am = (AlarmManager) 
getSystemService(ALARM_SERVICE);
+                        am.cancel(pendingRIntent);
+                        alarmIntents.remove(serverId);
+                    }
+                    ReconnectReceiver receiver = alarmReceivers.get(serverId);
+                    if (receiver != null) {
+                        unregisterReceiver(receiver);
+                        alarmReceivers.remove(serverId);
+                    }
+                }
             } else {
                 shutDown = false;
             }
@@ -516,6 +575,20 @@ public class IRCService extends Service
         if (foreground) {
             stopForegroundCompat(R.string.app_name);
         }
+
+        AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
+        synchronized(alarmIntentsLock) {
+            for (PendingIntent pendingRIntent : alarmIntents.values()) {
+                am.cancel(pendingRIntent);
+            }
+            for (ReconnectReceiver receiver : alarmReceivers.values()) {
+                unregisterReceiver(receiver);
+            }
+            alarmIntents.clear();
+            alarmIntents = null;
+            alarmReceivers.clear();
+            alarmReceivers = null;
+        }
     }
 
     /**
diff --git a/application/src/org/yaaic/model/Broadcast.java 
b/application/src/org/yaaic/model/Broadcast.java
index 97ca0fe..6049362 100644
--- a/application/src/org/yaaic/model/Broadcast.java
+++ b/application/src/org/yaaic/model/Broadcast.java
@@ -30,6 +30,7 @@ import android.content.Intent;
 public abstract class Broadcast
 {
     public static final String SERVER_UPDATE         = 
"org.yaaic.server.status";
+    public static final String SERVER_RECONNECT      = 
"org.yaaic.server.reconnect.";
 
     public static final String CONVERSATION_MESSAGE    = 
"org.yaaic.conversation.message";
     public static final String CONVERSATION_NEW        = 
"org.yaaic.conversation.new";
diff --git a/application/src/org/yaaic/model/Settings.java 
b/application/src/org/yaaic/model/Settings.java
index 349d631..7cd3a1f 100644
--- a/application/src/org/yaaic/model/Settings.java
+++ b/application/src/org/yaaic/model/Settings.java
@@ -132,6 +132,19 @@ public class Settings
     }
 
     /**
+     * Get the reconnect interval
+     * 
+     * @return The reconnect interval in minutes
+     */
+    public int getReconnectInterval()
+    {
+        return Integer.parseInt(preferences.getString(
+            resources.getString(R.string.key_reconnect_interval),
+            resources.getString(R.string.default_reconnect_interval)
+        ));
+    }
+
+    /**
      * Get the quit message
      * 
      * @return The message to display when the user disconnects
diff --git a/application/src/org/yaaic/receiver/ReconnectReceiver.java 
b/application/src/org/yaaic/receiver/ReconnectReceiver.java
new file mode 100644
index 0000000..64b3c96
--- /dev/null
+++ b/application/src/org/yaaic/receiver/ReconnectReceiver.java
@@ -0,0 +1,64 @@
+/*
+Yaaic - Yet Another Android IRC Client
+
+Copyright 2009-2011 Sebastian Kaspari
+Copyright 2011 Steven Luo
+
+This file is part of Yaaic.
+
+Yaaic is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Yaaic is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Yaaic.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.yaaic.receiver;
+
+import org.yaaic.irc.IRCService;
+import org.yaaic.model.Broadcast;
+import org.yaaic.model.Server;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * A receiver to listen for alarms and start a reconnect attempt
+ * 
+ * @author Steven Luo <[email protected]>
+ */
+public class ReconnectReceiver extends BroadcastReceiver
+{
+    private IRCService service;
+    private Server server;
+
+    /**
+     * Create a new reconnect receiver
+     * 
+     * @param server The server to reconnect to
+     */
+    public ReconnectReceiver(IRCService service, Server server)
+    {
+        this.service = service;
+        this.server = server;
+    }
+
+    /**
+     * On receive broadcast
+     */
+    @Override
+    public void onReceive(Context context, Intent intent)
+    {
+        if (!intent.getAction().equals(Broadcast.SERVER_RECONNECT + 
server.getId())) {
+            return;
+        }
+        service.connect(server);
+    }
+}
-- 
1.7.2.5

Reply via email to