1
0
mirror of https://github.com/moparisthebest/Yaaic synced 2024-11-22 17:02:21 -05:00

Overhaul notifications system

Features:
* Now displays the number of mentions that the user has not seen in the
  notification.
* When no mentions are outstanding, display which servers the user is
  connected to, not the last message.
* When more than one mention is outstanding, display the names of the
  conversations with new mentions, not just the last message received.
* Notifications of mentions are suppressed if you're in the conversation
  at the time of the mention.
* Notifications of mentions automatically clear when you bring up the
  conversation.
* Vibrate notifications now generate the user's chosen default vibrate
  pattern, not a hard-coded one.
* Add ticker text to the notification that's displayed when the IRCService
  goes into the foreground, instead of displaying a blank ticker.

To allow for all of this, the implementation moves most of the details
of generating the notification text into the IRCService, which now
exposes addNewMention() and notifyConnected()/notifyDisconnected()
methods instead of the lower-level updateNotification().
This commit is contained in:
Steven Luo 2011-05-29 17:49:07 -07:00 committed by Sebastian Kaspari
parent 09fedc6975
commit 61960c9add
7 changed files with 225 additions and 41 deletions

View File

@ -113,8 +113,10 @@
<string name="command_desc_voice">Give a user voice status</string>
<string name="command_desc_whois">Get information about a user</string>
<string name="notification_running">Yaaic is running</string>
<string name="notification_connected">Connected to %1$s</string>
<string name="notification_disconnected">Disconnected from %1$s</string>
<string name="notification_mentions">New messages in: %1$s</string>
<string name="message_connected">Connected to %1$s</string>
<string name="message_deop">%1$s deops %2$s</string>

View File

@ -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();
}

View File

@ -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,12 +245,15 @@ public class IRCConnection extends PircBot
boolean mentioned = isMentioned(action);
if (mentioned || target.equals(this.getNick())) {
service.updateNotification(
target + ": " + sender + " " + action,
if (conversation.getStatus() != Conversation.STATUS_SELECTED || !server.getIsForeground()) {
service.addNewMention(
conversation,
conversation.getName() + ": " + sender + " " + action,
service.getSettings().isVibrateHighlightEnabled(),
service.getSettings().isSoundHighlightEnabled()
);
}
}
if (mentioned) {
// highlight
@ -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(
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);
}
server.getConversation(target).addMessage(message);
conversation.setStatus(Conversation.STATUS_HIGHLIGHT);
}
conversation.addMessage(message);
Intent intent = Broadcast.createConversationIntent(
Broadcast.CONVERSATION_MESSAGE,
@ -619,11 +626,14 @@ public class IRCConnection extends PircBot
return;
}
service.updateNotification(
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);

View File

@ -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<Integer, IRCConnection> connections;
private boolean foreground = false;
private ArrayList<String> connectedServerTitles;
private LinkedHashMap<String, Conversation> 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<Integer, IRCConnection>();
this.binder = new IRCBinder(this);
this.connectedServerTitles = new ArrayList<String>();
this.mentions = new LinkedHashMap<String, Conversation>();
}
/**
@ -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.

View File

@ -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());
}

View File

@ -47,6 +47,7 @@ public abstract class Conversation
private final LinkedList<Message> 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;
}
}

View File

@ -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;
}
}