Skip to content

Commit

Permalink
Fix auto-reconnect
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
steven676 authored and pocmo committed Jun 29, 2011
1 parent e765131 commit bce2523
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 5 deletions.
16 changes: 16 additions & 0 deletions application/res/values/arrays.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
3 changes: 3 additions & 0 deletions application/res/values/settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down
3 changes: 3 additions & 0 deletions application/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
9 changes: 9 additions & 0 deletions application/res/xml/preferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down
83 changes: 78 additions & 5 deletions application/src/org/yaaic/irc/IRCService.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,16 @@
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
Expand Down Expand Up @@ -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
*/
Expand All @@ -92,6 +100,9 @@ public IRCService()
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();
}

/**
Expand Down Expand Up @@ -383,11 +394,31 @@ public void stopForegroundCompat(int id)
*/
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());
Expand Down Expand Up @@ -415,19 +446,33 @@ public void run() {
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);
Expand All @@ -436,7 +481,7 @@ public void run() {

Intent cIntent = Broadcast.createConversationIntent(
Broadcast.CONVERSATION_MESSAGE,
server.getId(),
serverId,
ServerInfo.DEFAULT_NAME
);
sendBroadcast(cIntent);
Expand Down Expand Up @@ -494,6 +539,20 @@ public void checkServiceStatus()
}
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;
}
Expand All @@ -516,6 +575,20 @@ public void onDestroy()
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;
}
}

/**
Expand Down
1 change: 1 addition & 0 deletions application/src/org/yaaic/model/Broadcast.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
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";
Expand Down
13 changes: 13 additions & 0 deletions application/src/org/yaaic/model/Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,19 @@ public boolean isReconnectEnabled()
);
}

/**
* 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
*
Expand Down
64 changes: 64 additions & 0 deletions application/src/org/yaaic/receiver/ReconnectReceiver.java
Original file line number Diff line number Diff line change
@@ -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);
}
}

0 comments on commit bce2523

Please sign in to comment.