From bce2523f98d48d11bf889c301d931f9d8bbdef86 Mon Sep 17 00:00:00 2001 From: Steven Luo Date: Wed, 29 Jun 2011 01:20:02 -0700 Subject: [PATCH] Fix auto-reconnect 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. --- 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 +++++++++++++++++-- .../src/org/yaaic/model/Broadcast.java | 1 + application/src/org/yaaic/model/Settings.java | 13 +++ .../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 @@ 19 20 + + 1 minute + 5 minutes + 10 minutes + 15 minutes + 20 minutes + 30 minutes + + + 1 + 5 + 10 + 15 + 20 + 30 + 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 @@ reconnect false + reconnect_interval + 5 + quitmessage Yaaic - Yet another Android IRC client - http://www.yaaic.org 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 @@ Connection Reconnect Automatically reconnect on disconnect + Reconnect interval + Number of minutes between reconnection tries + Reconnect interval Chat Show icons 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 . android:summary="@string/settings_reconnect_desc" android:key="@string/key_reconnect" android:defaultValue="@string/default_reconnect" /> + 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 alarmIntents; + private HashMap 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(); this.mentions = new LinkedHashMap(); + this.alarmIntents = new HashMap(); + this.alarmReceivers = new HashMap(); + 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 @@ -131,6 +131,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 * 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 . + */ +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 + */ +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); + } +}