diff --git a/application/res/values/strings.xml b/application/res/values/strings.xml
index 366112c..56c10cb 100644
--- a/application/res/values/strings.xml
+++ b/application/res/values/strings.xml
@@ -113,8 +113,10 @@
Give a user voice status
Get information about a user
+ Yaaic is running
Connected to %1$s
Disconnected from %1$s
+ New messages in: %1$s
Connected to %1$s
%1$s deops %2$s
diff --git a/application/src/org/yaaic/activity/ConversationActivity.java b/application/src/org/yaaic/activity/ConversationActivity.java
index be27f33..8f4d6a6 100644
--- a/application/src/org/yaaic/activity/ConversationActivity.java
+++ b/application/src/org/yaaic/activity/ConversationActivity.java
@@ -154,7 +154,7 @@ public class ConversationActivity extends Activity implements ServiceConnection,
deckAdapter = new DeckAdapter();
deck = (Gallery) findViewById(R.id.deck);
- deck.setOnItemSelectedListener(new ConversationSelectedListener(server, (TextView) findViewById(R.id.title), dots));
+ deck.setOnItemSelectedListener(new ConversationSelectedListener(this, server, (TextView) findViewById(R.id.title), dots));
deck.setAdapter(deckAdapter);
deck.setOnItemClickListener(new ConversationClickListener(deckAdapter, switcher));
deck.setBackgroundDrawable(new NonScalingBackgroundDrawable(this, deck, R.drawable.background));
@@ -244,6 +244,14 @@ public class ConversationActivity extends Activity implements ServiceConnection,
mAdapter.addBulkMessages(conversation.getBuffer());
conversation.clearBuffer();
}
+
+ // Clear new message notifications for the selected conversation
+ if (conversation.getStatus() == Conversation.STATUS_SELECTED && conversation.getNewMentions() > 0) {
+ Intent ackIntent = new Intent(this, IRCService.class);
+ ackIntent.setAction(IRCService.ACTION_ACK_NEW_MENTIONS);
+ ackIntent.putExtra(IRCService.EXTRA_ACK_CONVTITLE, conversation.getName());
+ startService(ackIntent);
+ }
}
// Join channel that has been selected in JoinActivity (onActivityResult())
@@ -256,6 +264,8 @@ public class ConversationActivity extends Activity implements ServiceConnection,
}
}.start();
}
+
+ server.setIsForeground(true);
}
/**
@@ -266,6 +276,8 @@ public class ConversationActivity extends Activity implements ServiceConnection,
{
super.onPause();
+ server.setIsForeground(false);
+
if (binder != null && binder.getService() != null) {
binder.getService().checkServiceStatus();
}
diff --git a/application/src/org/yaaic/irc/IRCConnection.java b/application/src/org/yaaic/irc/IRCConnection.java
index fe57eaa..c1cdc13 100644
--- a/application/src/org/yaaic/irc/IRCConnection.java
+++ b/application/src/org/yaaic/irc/IRCConnection.java
@@ -141,7 +141,7 @@ public class IRCConnection extends PircBot
Broadcast.createServerIntent(Broadcast.SERVER_UPDATE, server.getId())
);
- service.updateNotification(service.getString(R.string.notification_connected, server.getTitle()));
+ service.notifyConnected(server.getTitle());
Message message = new Message(service.getString(R.string.message_connected, server.getTitle()));
message.setColor(Message.COLOR_GREEN);
@@ -245,11 +245,14 @@ public class IRCConnection extends PircBot
boolean mentioned = isMentioned(action);
if (mentioned || target.equals(this.getNick())) {
- service.updateNotification(
- target + ": " + sender + " " + action,
- service.getSettings().isVibrateHighlightEnabled(),
- service.getSettings().isSoundHighlightEnabled()
- );
+ if (conversation.getStatus() != Conversation.STATUS_SELECTED || !server.getIsForeground()) {
+ service.addNewMention(
+ conversation,
+ conversation.getName() + ": " + sender + " " + action,
+ service.getSettings().isVibrateHighlightEnabled(),
+ service.getSettings().isSoundHighlightEnabled()
+ );
+ }
}
if (mentioned) {
@@ -410,20 +413,24 @@ public class IRCConnection extends PircBot
protected void onMessage(String target, String sender, String login, String hostname, String text)
{
Message message = new Message(text, sender);
+ Conversation conversation = server.getConversation(target);
if (isMentioned(text)) {
// highlight
message.setColor(Message.COLOR_RED);
- service.updateNotification(
- target + ": <" + sender + "> " + text,
- service.getSettings().isVibrateHighlightEnabled(),
- service.getSettings().isSoundHighlightEnabled()
- );
+ if (conversation.getStatus() != Conversation.STATUS_SELECTED || !server.getIsForeground()) {
+ service.addNewMention(
+ conversation,
+ target + ": <" + sender + "> " + text,
+ service.getSettings().isVibrateHighlightEnabled(),
+ service.getSettings().isSoundHighlightEnabled()
+ );
+ }
- server.getConversation(target).setStatus(Conversation.STATUS_HIGHLIGHT);
+ conversation.setStatus(Conversation.STATUS_HIGHLIGHT);
}
- server.getConversation(target).addMessage(message);
+ conversation.addMessage(message);
Intent intent = Broadcast.createConversationIntent(
Broadcast.CONVERSATION_MESSAGE,
@@ -619,11 +626,14 @@ public class IRCConnection extends PircBot
return;
}
- service.updateNotification(
- "<" + sender + "> " + text,
- service.getSettings().isVibrateHighlightEnabled(),
- service.getSettings().isSoundHighlightEnabled()
- );
+ if (conversation.getStatus() != Conversation.STATUS_SELECTED || !server.getIsForeground()) {
+ service.addNewMention(
+ conversation,
+ "<" + sender + "> " + text,
+ service.getSettings().isVibrateHighlightEnabled(),
+ service.getSettings().isSoundHighlightEnabled()
+ );
+ }
if (isMentioned(text)) {
message.setColor(Message.COLOR_RED);
@@ -1099,7 +1109,7 @@ public class IRCConnection extends PircBot
server.setStatus(Status.DISCONNECTED);
}
- service.updateNotification(service.getString(R.string.notification_disconnected, server.getTitle()));
+ service.notifyDisconnected(server.getTitle());
Intent sIntent = Broadcast.createServerIntent(Broadcast.SERVER_UPDATE, server.getId());
service.sendBroadcast(sIntent);
diff --git a/application/src/org/yaaic/irc/IRCService.java b/application/src/org/yaaic/irc/IRCService.java
index 499de71..4da5799 100644
--- a/application/src/org/yaaic/irc/IRCService.java
+++ b/application/src/org/yaaic/irc/IRCService.java
@@ -24,6 +24,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import org.jibble.pircbot.IrcException;
import org.jibble.pircbot.NickAlreadyInUseException;
@@ -32,6 +33,7 @@ import org.yaaic.Yaaic;
import org.yaaic.activity.ServersActivity;
import org.yaaic.db.Database;
import org.yaaic.model.Broadcast;
+import org.yaaic.model.Conversation;
import org.yaaic.model.Message;
import org.yaaic.model.Server;
import org.yaaic.model.ServerInfo;
@@ -54,6 +56,11 @@ public class IRCService extends Service
private final IRCBinder binder;
private final HashMap connections;
private boolean foreground = false;
+ private ArrayList connectedServerTitles;
+ private LinkedHashMap mentions;
+ private int newMentions = 0;
+
+ private static final int FOREGROUND_NOTIFICATION = 1;
@SuppressWarnings("rawtypes")
private static final Class[] mStartForegroundSignature = new Class[] { int.class, Notification.class };
@@ -62,6 +69,8 @@ public class IRCService extends Service
public static final String ACTION_FOREGROUND = "org.yaaic.service.foreground";
public static final String ACTION_BACKGROUND = "org.yaaic.service.background";
+ public static final String ACTION_ACK_NEW_MENTIONS = "org.yaaic.service.ack_new_mentions";
+ public static final String EXTRA_ACK_CONVTITLE = "org.yaaic.service.ack_convtitle";
private NotificationManager notificationManager;
private Method mStartForeground;
@@ -80,6 +89,8 @@ public class IRCService extends Service
this.connections = new HashMap();
this.binder = new IRCBinder(this);
+ this.connectedServerTitles = new ArrayList();
+ this.mentions = new LinkedHashMap();
}
/**
@@ -166,7 +177,7 @@ public class IRCService extends Service
foreground = true;
// Set the icon, scrolling text and timestamp
- notification = new Notification(R.drawable.icon, "", System.currentTimeMillis());
+ notification = new Notification(R.drawable.icon, getText(R.string.notification_running), System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects this notification
Intent notifyIntent = new Intent(this, ServersActivity.class);
@@ -176,51 +187,146 @@ public class IRCService extends Service
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(this, getText(R.string.app_name), "", contentIntent);
- startForegroundCompat(R.string.app_name, notification);
+ startForegroundCompat(FOREGROUND_NOTIFICATION, notification);
} else if (ACTION_BACKGROUND.equals(intent.getAction()) && !foreground) {
- stopForegroundCompat(R.string.app_name);
+ stopForegroundCompat(FOREGROUND_NOTIFICATION);
+ } else if (ACTION_ACK_NEW_MENTIONS.equals(intent.getAction())) {
+ ackNewMentions(intent.getStringExtra(EXTRA_ACK_CONVTITLE));
}
}
- /**
- * Update notification
- *
- * @param text The text to display
- */
- public void updateNotification(String text)
- {
- updateNotification(text, false, false);
- }
-
/**
* Update notification and vibrate if needed
*
- * @param text The text to display
+ * @param text The ticker text to display
+ * @param contentText The text to display in the notification dropdown
* @param vibrate True if the device should vibrate, false otherwise
+ * @param sound True if the device should make sound, false otherwise
*/
- public void updateNotification(String text, boolean vibrate, boolean sound)
+ private void updateNotification(String text, String contentText, boolean vibrate, boolean sound)
{
if (foreground) {
- notificationManager.cancel(R.string.app_name);
notification = new Notification(R.drawable.icon, text, System.currentTimeMillis());
Intent notifyIntent = new Intent(this, ServersActivity.class);
notifyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notifyIntent, 0);
- notification.setLatestEventInfo(this, getText(R.string.app_name), text, contentIntent);
+
+ if (contentText == null) {
+ if (!connectedServerTitles.isEmpty()) {
+ StringBuilder sb = new StringBuilder();
+ for (String title : connectedServerTitles) {
+ sb.append(title + ", ");
+ }
+ contentText = getString(R.string.notification_connected, sb.substring(0, sb.length()-2));
+ } else {
+ contentText = "";
+ }
+ }
+
+ notification.setLatestEventInfo(this, getText(R.string.app_name), contentText, contentIntent);
if (vibrate) {
- long[] pattern = {0,100,200,300};
- notification.vibrate = pattern;
+ notification.defaults |= Notification.DEFAULT_VIBRATE;
}
if (sound) {
notification.defaults |= Notification.DEFAULT_SOUND;
}
- notificationManager.notify(R.string.app_name, notification);
+ notification.number = newMentions;
+
+ notificationManager.notify(FOREGROUND_NOTIFICATION, notification);
}
}
+ /**
+ * Update the status bar notification for a new mention
+ */
+ private void notifyMention(String msg, boolean vibrate, boolean sound)
+ {
+ String contentText = null;
+
+ if (newMentions == 1 && msg != null) {
+ contentText = msg;
+ } else if (newMentions >= 1) {
+ StringBuilder sb = new StringBuilder();
+ for (Conversation conv : mentions.values()) {
+ sb.append(conv.getName() + " (" + conv.getNewMentions() + "), ");
+ }
+ contentText = getString(R.string.notification_mentions, sb.substring(0, sb.length()-2));
+ }
+
+ updateNotification(msg, contentText, vibrate, sound);
+ }
+
+ /**
+ * Notify the service of a new mention (updates the status bar notification)
+ *
+ * @param conversation The conversation where the new mention occurred
+ * @param msg The text of the new message
+ * @param vibrate Whether the notification should include vibration
+ * @param sound Whether the notification should include sound
+ */
+ public void addNewMention(Conversation conversation, String msg, boolean vibrate, boolean sound)
+ {
+ if (conversation == null)
+ return;
+
+ String convTitle = conversation.getName();
+
+ conversation.addNewMention();
+ ++newMentions;
+ if (!mentions.containsKey(convTitle)) {
+ mentions.put(convTitle, conversation);
+ }
+
+ notifyMention(msg, vibrate, sound);
+ }
+
+ /**
+ * Notify the service that new mentions have been viewed (updates the status bar notification)
+ *
+ * @param convTitle The title of the conversation whose new mentions have been read
+ */
+ public void ackNewMentions(String convTitle)
+ {
+ if (convTitle == null)
+ return;
+
+ Conversation conversation = mentions.remove(convTitle);
+ if (conversation == null)
+ return;
+ newMentions -= conversation.getNewMentions();
+ conversation.clearNewMentions();
+ if (newMentions < 0)
+ newMentions = 0;
+
+ notifyMention(null, false, false);
+ }
+
+ /**
+ * Notify the service of connection to a server (updates the status bar notification)
+ *
+ * @param title The title of the newly connected server
+ */
+ public void notifyConnected(String title)
+ {
+ connectedServerTitles.add(title);
+ updateNotification(getString(R.string.notification_connected, title), null, false, false);
+ }
+
+ /**
+ * Notify the service of disconnection from a server (updates the status bar notification)
+ *
+ * @param title The title of the disconnected server
+ */
+ public void notifyDisconnected(String title)
+ {
+ connectedServerTitles.remove(title);
+ updateNotification(getString(R.string.notification_disconnected, title), null, false, false);
+ }
+
+
/**
* This is a wrapper around the new startForeground method, using the older
* APIs if it is not available.
diff --git a/application/src/org/yaaic/listener/ConversationSelectedListener.java b/application/src/org/yaaic/listener/ConversationSelectedListener.java
index 1363614..5a83a93 100644
--- a/application/src/org/yaaic/listener/ConversationSelectedListener.java
+++ b/application/src/org/yaaic/listener/ConversationSelectedListener.java
@@ -24,11 +24,14 @@ import org.yaaic.model.Channel;
import org.yaaic.model.Conversation;
import org.yaaic.model.Server;
import org.yaaic.view.ConversationSwitcher;
+import org.yaaic.irc.IRCService;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.TextView;
+import android.content.Context;
+import android.content.Intent;
/**
* Listener for conversation selections
@@ -37,6 +40,7 @@ import android.widget.TextView;
*/
public class ConversationSelectedListener implements OnItemSelectedListener
{
+ private final Context ctx;
private final Server server;
private final TextView titleView;
private final ConversationSwitcher switcher;
@@ -47,8 +51,9 @@ public class ConversationSelectedListener implements OnItemSelectedListener
* @param server
* @param titleView
*/
- public ConversationSelectedListener(Server server, TextView titleView, ConversationSwitcher switcher)
+ public ConversationSelectedListener(Context ctx, Server server, TextView titleView, ConversationSwitcher switcher)
{
+ this.ctx = ctx;
this.server = server;
this.titleView = titleView;
this.switcher = switcher;
@@ -80,6 +85,13 @@ public class ConversationSelectedListener implements OnItemSelectedListener
previousConversation.setStatus(Conversation.STATUS_DEFAULT);
}
+ if (conversation.getNewMentions() > 0) {
+ Intent i = new Intent(ctx, IRCService.class);
+ i.setAction(IRCService.ACTION_ACK_NEW_MENTIONS);
+ i.putExtra(IRCService.EXTRA_ACK_CONVTITLE, conversation.getName());
+ ctx.startService(i);
+ }
+
conversation.setStatus(Conversation.STATUS_SELECTED);
server.setSelectedConversation(conversation.getName());
}
diff --git a/application/src/org/yaaic/model/Conversation.java b/application/src/org/yaaic/model/Conversation.java
index 43f3013..fc83922 100644
--- a/application/src/org/yaaic/model/Conversation.java
+++ b/application/src/org/yaaic/model/Conversation.java
@@ -47,6 +47,7 @@ public abstract class Conversation
private final LinkedList history;
private final String name;
private int status = 1;
+ private int newMentions = 0;
/**
* Get the type of conversation (channel, query, ..)
@@ -179,4 +180,28 @@ public abstract class Conversation
{
return status;
}
+
+ /**
+ * Increment the count of unread mentions in this conversation
+ */
+ public void addNewMention()
+ {
+ ++newMentions;
+ }
+
+ /**
+ * Mark all new mentions as unread
+ */
+ public void clearNewMentions()
+ {
+ newMentions = 0;
+ }
+
+ /**
+ * Get the number of unread mentions in this conversation
+ */
+ public int getNewMentions()
+ {
+ return newMentions;
+ }
}
diff --git a/application/src/org/yaaic/model/Server.java b/application/src/org/yaaic/model/Server.java
index e28e405..2053826 100644
--- a/application/src/org/yaaic/model/Server.java
+++ b/application/src/org/yaaic/model/Server.java
@@ -48,6 +48,7 @@ public class Server
private int status = Status.DISCONNECTED;
private String selected = "";
+ private boolean isForeground = false;
/**
* Create a new server object
@@ -404,4 +405,20 @@ public class Server
return R.drawable.connecting;
}
+
+ /**
+ * Get whether a ConversationActivity for this server is currently in the foreground.
+ */
+ public boolean getIsForeground()
+ {
+ return isForeground;
+ }
+
+ /**
+ * Set whether a ConversationActivity for this server is currently in the foreground.
+ */
+ public void setIsForeground(boolean isForeground)
+ {
+ this.isForeground = isForeground;
+ }
}