diff --git a/resources/colors/colors.properties b/resources/colors/colors.properties index 214bf1cf3..f4eb28413 100644 --- a/resources/colors/colors.properties +++ b/resources/colors/colors.properties @@ -79,11 +79,8 @@ service.gui.TAB_TITLE_SELECTED=000000 # Custom components GUI constants -# Background color for incoming call lines in the call list. -service.gui.HISTORY_INCOMING_CALL_BACKGROUND=F9FFC5 - -# Background color for outgoing call lines in the call list. -service.gui.HISTORY_OUTGOING_CALL_BACKGROUND=F3F4F7 +# Background color for even records in call history +service.gui.CALL_HISTORY_EVEN_ROW_COLOR=EFEFEF # Selection color for all lists (contact list, call list, chat rooms list, etc.) service.gui.LIST_SELECTION_COLOR=ADD2EF diff --git a/resources/images/images.properties b/resources/images/images.properties index e67420a58..444228d7a 100644 --- a/resources/images/images.properties +++ b/resources/images/images.properties @@ -96,6 +96,7 @@ service.gui.icons.CHANGE_ROOM_SUBJECT_16x16=resources/images/impl/gui/common/cha service.gui.icons.CHANGE_NICKNAME_16x16=resources/images/impl/gui/common/changeNickname16x16.png service.gui.icons.BAN_16x16=resources/images/impl/gui/common/ban16x16.png service.gui.icons.KICK_16x16=resources/images/impl/gui/common/kick16x16.png +service.gui.icons.VOICEMAIL=resources/images/impl/gui/common/voicemail.png # Status icons service.gui.statusicons.USER_ONLINE_ICON=resources/images/impl/gui/common/statusicons/online.png diff --git a/resources/images/impl/gui/common/incomingCall.png b/resources/images/impl/gui/common/incomingCall.png index 85f68886d..bbcc12cee 100644 Binary files a/resources/images/impl/gui/common/incomingCall.png and b/resources/images/impl/gui/common/incomingCall.png differ diff --git a/resources/images/impl/gui/common/missedCall.png b/resources/images/impl/gui/common/missedCall.png index d3100a4e3..5bc2e9691 100644 Binary files a/resources/images/impl/gui/common/missedCall.png and b/resources/images/impl/gui/common/missedCall.png differ diff --git a/resources/images/impl/gui/common/outgoingCall.png b/resources/images/impl/gui/common/outgoingCall.png index cde812231..849fec4a6 100644 Binary files a/resources/images/impl/gui/common/outgoingCall.png and b/resources/images/impl/gui/common/outgoingCall.png differ diff --git a/resources/images/impl/gui/common/voicemail.png b/resources/images/impl/gui/common/voicemail.png new file mode 100644 index 000000000..737c73b90 Binary files /dev/null and b/resources/images/impl/gui/common/voicemail.png differ diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties index 83feeccc4..3f0a8da37 100644 --- a/resources/languages/resources.properties +++ b/resources/languages/resources.properties @@ -253,7 +253,7 @@ service.gui.LOGIN_WINDOW_TITLE=Login {0} service.gui.LOGOFF_NOT_SUCCEEDED=An error occurred while logging off with the following account: User name: {0}, Server name: {1}. service.gui.MEMBER=member service.gui.MESSAGE=Message: -service.gui.MISSED_CALLS_TOOL_TIP=You have missed calls from: +service.gui.MISSED_CALLS_TOOL_TIP=Missed calls from: service.gui.MISSED_CALLS_MORE_TOOL_TIP= and {0} more service.gui.MODERATOR=moderator service.gui.MORE=See more @@ -413,6 +413,13 @@ service.gui.VIEW_HISTORY=View &history service.gui.VIEW_SMILEYS=View &smileys service.gui.VIEW_STYLEBAR=View style &bar service.gui.VIEW_TOOLBAR=View &toolbar +service.gui.VOICEMAIL_NEW_OLD_RECEIVED={0} new and {1} old messages +service.gui.VOICEMAIL_NEW_RECEIVED={0} new messages +service.gui.VOICEMAIL_OLD_RECEIVED={0} old messages +service.gui.VOICEMAIL_NO_MESSAGES=No messages. +service.gui.VOICEMAIL_TITLE=Voice messages +service.gui.VOICEMAIL_TOOLTIP=Voice messages for: +service.gui.VOICEMAIL_TIP=Click the call button to hear your messages. service.gui.WARNING=Warning service.gui.YES=Yes service.gui.YESTERDAY=Yesterday diff --git a/src/net/java/sip/communicator/impl/gui/main/MainFrame.java b/src/net/java/sip/communicator/impl/gui/main/MainFrame.java index 32a65c389..57cc2f7f2 100644 --- a/src/net/java/sip/communicator/impl/gui/main/MainFrame.java +++ b/src/net/java/sip/communicator/impl/gui/main/MainFrame.java @@ -29,6 +29,7 @@ import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.gui.Container; import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.OperationSetMessageWaiting.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.service.resources.*; import net.java.sip.communicator.util.*; @@ -144,6 +145,16 @@ public class MainFrame */ private ContactListPane contactListPanel; + /** + * The user interface provider presence listener. + */ + private ProviderPresenceStatusListener uiProviderPresenceListener; + + /** + * The user interface call listener. + */ + private CallListener uiCallListener; + /** * Creates an instance of MainFrame. */ @@ -377,10 +388,13 @@ public void addProtocolSupportedOperationSets( supportedOperationSets.get(pOpSetClassName); } + uiProviderPresenceListener + = new GUIProviderPresenceStatusListener(); + presence.addProviderPresenceStatusListener( - new GUIProviderPresenceStatusListener()); + uiProviderPresenceListener); presence.addContactPresenceStatusListener( - GuiActivator.getContactList()); + GuiActivator.getContactList()); } // Obtain the basic instant messaging operation set. @@ -424,7 +438,9 @@ public void addProtocolSupportedOperationSets( = (OperationSetBasicTelephony) supportedOperationSets.get(telOpSetClassName); - telephony.addCallListener(new CallManager.GuiCallListener()); + uiCallListener = new CallManager.GuiCallListener(); + + telephony.addCallListener(uiCallListener); } // Obtain the multi user chat operation set. @@ -467,6 +483,148 @@ public void addProtocolSupportedOperationSets( { fileTransferOpSet.addFileTransferListener(getContactListPanel()); } + + OperationSetMessageWaiting messageWaiting + = protocolProvider.getOperationSet(OperationSetMessageWaiting.class); + + if (messageWaiting != null) + { + messageWaiting.addMessageWaitingNotificationListener( + MessageType.VOICE, + TreeContactList.getNotificationContactSource()); + } + } + + /** + * Removes all protocol supported operation sets. + * + * @param protocolProvider The protocol provider. + */ + public void removeProtocolSupportedOperationSets( + ProtocolProviderService protocolProvider) + { + Map supportedOperationSets + = protocolProvider.getSupportedOperationSets(); + + String ppOpSetClassName = OperationSetPersistentPresence + .class.getName(); + String pOpSetClassName = OperationSetPresence.class.getName(); + + // Obtain the presence operation set. + if (supportedOperationSets.containsKey(ppOpSetClassName) + || supportedOperationSets.containsKey(pOpSetClassName)) + { + OperationSetPresence presence = (OperationSetPresence) + supportedOperationSets.get(ppOpSetClassName); + + if(presence == null) + { + presence = (OperationSetPresence) + supportedOperationSets.get(pOpSetClassName); + } + + if (uiProviderPresenceListener != null) + presence.removeProviderPresenceStatusListener( + uiProviderPresenceListener); + presence.removeContactPresenceStatusListener( + GuiActivator.getContactList()); + } + + // Obtain the basic instant messaging operation set. + String imOpSetClassName = OperationSetBasicInstantMessaging + .class.getName(); + + if (supportedOperationSets.containsKey(imOpSetClassName)) + { + OperationSetBasicInstantMessaging im + = (OperationSetBasicInstantMessaging) + supportedOperationSets.get(imOpSetClassName); + + im.removeMessageListener(getContactListPanel()); + } + + // Obtain the typing notifications operation set. + String tnOpSetClassName = OperationSetTypingNotifications + .class.getName(); + + if (supportedOperationSets.containsKey(tnOpSetClassName)) + { + OperationSetTypingNotifications tn + = (OperationSetTypingNotifications) + supportedOperationSets.get(tnOpSetClassName); + + //Add to all typing notification operation sets the Message + //listener implemented in the ContactListPanel, which handles + //all received messages. + tn.removeTypingNotificationsListener(this.getContactListPanel()); + } + + // Obtain the basic telephony operation set. + String telOpSetClassName = OperationSetBasicTelephony.class.getName(); + + if (supportedOperationSets.containsKey(telOpSetClassName)) + { + OperationSetBasicTelephony telephony + = (OperationSetBasicTelephony) + supportedOperationSets.get(telOpSetClassName); + + if (uiCallListener != null) + telephony.removeCallListener(uiCallListener); + } + + // Obtain the multi user chat operation set. + String multiChatClassName = OperationSetMultiUserChat.class.getName(); + + if (supportedOperationSets.containsKey(multiChatClassName)) + { + OperationSetMultiUserChat multiUserChat + = (OperationSetMultiUserChat) + supportedOperationSets.get(multiChatClassName); + + ConferenceChatManager conferenceManager + = GuiActivator.getUIService().getConferenceChatManager(); + + multiUserChat.removeInvitationListener(conferenceManager); + multiUserChat.removeInvitationRejectionListener(conferenceManager); + multiUserChat.removePresenceListener(conferenceManager); + } + + // Obtain the ad-hoc multi user chat operation set. + OperationSetAdHocMultiUserChat adHocMultiChatOpSet + = protocolProvider + .getOperationSet(OperationSetAdHocMultiUserChat.class); + + if (adHocMultiChatOpSet != null) + { + ConferenceChatManager conferenceManager + = GuiActivator.getUIService().getConferenceChatManager(); + + adHocMultiChatOpSet + .removeInvitationListener(conferenceManager); + adHocMultiChatOpSet + .removeInvitationRejectionListener(conferenceManager); + adHocMultiChatOpSet + .removePresenceListener(conferenceManager); + } + + // Obtain file transfer operation set. + OperationSetFileTransfer fileTransferOpSet + = protocolProvider.getOperationSet(OperationSetFileTransfer.class); + + if (fileTransferOpSet != null) + { + fileTransferOpSet.removeFileTransferListener(getContactListPanel()); + } + + OperationSetMessageWaiting messageWaiting + = protocolProvider.getOperationSet(OperationSetMessageWaiting.class); + + if (messageWaiting != null) + { + messageWaiting.removeMessageWaitingNotificationListener( + MessageType.VOICE, + TreeContactList.getNotificationContactSource()); + } } /** @@ -509,7 +667,7 @@ public void addProtocolProvider(ProtocolProviderService protocolProvider) { if (logger.isTraceEnabled()) logger.trace("Add the following protocol provider to the gui: " - + protocolProvider.getAccountID().getAccountAddress()); + + protocolProvider.getAccountID().getAccountAddress()); this.protocolProviders.put(protocolProvider, initiateProviderIndex(protocolProvider)); @@ -527,6 +685,31 @@ public void addProtocolProvider(ProtocolProviderService protocolProvider) this.addProviderContactHandler(protocolProvider, contactHandler); } + /** + * Adds an account to the application. + * + * @param protocolProvider The protocol provider of the account. + */ + public void removeProtocolProvider(ProtocolProviderService protocolProvider) + { + if (logger.isTraceEnabled()) + logger.trace("Remove the following protocol provider to the gui: " + + protocolProvider.getAccountID().getAccountAddress()); + + this.protocolProviders.remove(protocolProvider); + + this.removeProtocolSupportedOperationSets(protocolProvider); + + removeProviderContactHandler(protocolProvider); + + this.updateProvidersIndexes(protocolProvider); + + if (accountStatusPanel.containsAccount(protocolProvider)) + { + accountStatusPanel.removeAccount(protocolProvider); + } + } + /** * Returns the index of the given protocol provider. * @param protocolProvider the protocol provider to search for @@ -561,22 +744,6 @@ public void addAccount(ProtocolProviderService protocolProvider) } } - /** - * Adds an account to the application. - * - * @param protocolProvider The protocol provider of the account. - */ - public void removeProtocolProvider(ProtocolProviderService protocolProvider) - { - this.protocolProviders.remove(protocolProvider); - this.updateProvidersIndexes(protocolProvider); - - if (accountStatusPanel.containsAccount(protocolProvider)) - { - accountStatusPanel.removeAccount(protocolProvider); - } - } - /** * Returns the account user id for the given protocol provider. * @param protocolProvider the protocol provider corresponding to the @@ -954,9 +1121,14 @@ public MainMenu getMainMenu() } /** + * Adds the given contactHandler to handle contact events for the + * given protocolProvider. * - * @param protocolProvider - * @param contactHandler + * @param protocolProvider the ProtocolProviderService, which + * contacts should be handled by the given contactHandler + * @param contactHandler the ContactEventHandler that would handle + * events coming from the UI for any contacts belonging to the given + * provider */ public void addProviderContactHandler( ProtocolProviderService protocolProvider, @@ -965,6 +1137,19 @@ public void addProviderContactHandler( providerContactHandlers.put(protocolProvider, contactHandler); } + /** + * Removes the ContactEventHandler corresponding to the given + * protocolProvider. + * + * @param protocolProvider the protocol provider, which contact handler + * we would like to remove + */ + public void removeProviderContactHandler( + ProtocolProviderService protocolProvider) + { + providerContactHandlers.remove(protocolProvider); + } + /** * Returns the ContactEventHandler registered for this protocol * provider. diff --git a/src/net/java/sip/communicator/impl/gui/main/UINotification.java b/src/net/java/sip/communicator/impl/gui/main/UINotification.java new file mode 100644 index 000000000..9b88a2bf4 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/UINotification.java @@ -0,0 +1,83 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.gui.main; + +import java.util.*; + +/** + * The UINotification class represents a notification received in the + * user interface. This could be a missed call, voicemail, email notification or + * any other notification. + * + * @author Yana Stamcheva + */ +public class UINotification +{ + /** + * The parent notification group. + */ + private final UINotificationGroup parentGroup; + + /** + * The name associated with this notification. + */ + private final String notificationName; + + /** + * The time when the notification was received. + */ + private final Date notificationTime; + + /** + * Creates an instance of UINotification by specifying the + * notification name and time. + * + * notification belongs + * @param displayName the name associated to this notification + * @param time the time when the notification was received + * @param parentGroup the group of notifications, to which this notification + * belongs + */ + public UINotification( String displayName, + Date time, + UINotificationGroup parentGroup) + { + this.notificationName = displayName; + this.notificationTime = time; + this.parentGroup = parentGroup; + } + + /** + * Returns the name associated with this notification. + * + * @return the name associated with this notification + */ + public String getDisplayName() + { + return notificationName; + } + + /** + * Returns the time the notification was received. + * + * @return the time the notification was received + */ + public Date getTime() + { + return notificationTime; + } + + /** + * Returns the parent notification group. + * + * @return the parent notification group + */ + public UINotificationGroup getGroup() + { + return parentGroup; + } +} diff --git a/src/net/java/sip/communicator/impl/gui/main/UINotificationGroup.java b/src/net/java/sip/communicator/impl/gui/main/UINotificationGroup.java new file mode 100644 index 000000000..35586613e --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/UINotificationGroup.java @@ -0,0 +1,114 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.gui.main; + +import java.util.*; + +/** + * The UINotificationGroup class represents a group of notifications. + * + * @author Yana Stamcheva + */ +public class UINotificationGroup +{ + /** + * A list of all unread notifications. + */ + private Collection unreadNotifications + = new ArrayList(); + + /** + * The name of the group to which this notification belongs. + */ + private final String groupName; + + /** + * The display name of the group to which this notification belongs. + */ + private final String groupDisplayName; + + /** + * + * @param groupName the name of the group to which this notification belongs + * @param groupDisplayName the display name of the group to which this + */ + public UINotificationGroup(String groupName, String groupDisplayName) + { + this.groupName = groupName; + this.groupDisplayName = groupDisplayName; + } + + /** + * Returns the name of the group, to which this notification belongs. + * + * @return the name of the group, to which this notification belongs + */ + public String getGroupName() + { + return groupName; + } + + /** + * Returns the display name of the group, to which this notification + * belongs. + * + * @return the display name of the group, to which this notification + * belongs + */ + public String getGroupDisplayName() + { + return groupDisplayName; + } + + /** + * Adds the given notification to the list of unread notifications and + * notifies interested listeners. + * + * @param notification the UINotification to add + */ + public void addNotification(UINotification notification) + { + synchronized (unreadNotifications) + { + unreadNotifications.add(notification); + } + } + + /** + * Removes all unread notifications. + */ + public void removeAllNotifications() + { + synchronized (unreadNotifications) + { + unreadNotifications.clear(); + } + } + + /** + * Returns a list of all unread notifications. + * + * @return a list of all unread notifications + */ + public Iterator getUnreadNotifications() + { + return new ArrayList(unreadNotifications).iterator(); + } + + /** + * Returns the count of unread notifications for this group. + * + * @return the count of unread notifications for this group + */ + public int getUnreadNotificationsCount() + { + synchronized (unreadNotifications) + { + return unreadNotifications.size(); + } + } +} diff --git a/src/net/java/sip/communicator/impl/gui/main/UINotificationListener.java b/src/net/java/sip/communicator/impl/gui/main/UINotificationListener.java new file mode 100644 index 000000000..52f583dfe --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/UINotificationListener.java @@ -0,0 +1,24 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.gui.main; + +/** + * The UINotificationListener listens for new notifications received + * in the user interface. Notifications could be for example missed calls, + * voicemails or email messages. + * + * @author Yana Stamcheva + */ +public interface UINotificationListener +{ + /** + * Indicates that a new notification is received. + * + * @param notification the notification that was received + */ + public void notificationReceived(UINotification notification); +} diff --git a/src/net/java/sip/communicator/impl/gui/main/UINotificationManager.java b/src/net/java/sip/communicator/impl/gui/main/UINotificationManager.java new file mode 100644 index 000000000..37d75f233 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/UINotificationManager.java @@ -0,0 +1,142 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.gui.main; + +import java.util.*; + +/** + * The UINotificationManager manages all notifications dedicated to + * be shown in the main application window. These could be missed calls, voice + * messages, etc. + * + * @author Yana Stamcheva + */ +public class UINotificationManager +{ + /** + * The list of all notification groups. + */ + private static Collection notificationGroups + = new ArrayList(); + + /** + * Listener notified for changes in missed calls count. + */ + private static Collection notificationListeners + = new ArrayList(); + + /** + * Adds the given UINotificationListener to the list of listeners + * that would be notified on any changes in missed calls count. + * + * @param l the UINotificationListener to add + */ + public static void addNotificationListener(UINotificationListener l) + { + synchronized (l) + { + notificationListeners.add(l); + } + } + + /** + * Removes the given UINotificationListener from the list of + * listeners that are notified on any changes in missed calls count. + * + * @param l the UINotificationListener to remove + */ + public static void removeNotificationListener(UINotificationListener l) + { + synchronized (l) + { + notificationListeners.remove(l); + } + } + + /** + * Adds the given notification to the list of unread notifications and + * notifies interested listeners. + * + * @param notification the UINotification to add + */ + public static void addNotification(UINotification notification) + { + UINotificationGroup group = notification.getGroup(); + + if (!notificationGroups.contains(group)) + notificationGroups.add(group); + + group.addNotification(notification); + + fireNotificationEvent(notification); + } + + /** + * Removes all unread notifications. + * + * @param group removes all unread notifications for the given notification + * group + */ + public static void removeAllNotifications(UINotificationGroup group) + { + group.removeAllNotifications(); + } + + /** + * Removes all unread notifications from all notification groups. + */ + public static void removeAllNotifications() + { + Iterator groups = notificationGroups.iterator(); + + while (groups.hasNext()) + { + groups.next().removeAllNotifications(); + } + } + + /** + * Returns a list of all unread notifications. + * + * @param group the notification group, which notification we're looking + * for + * @return a list of all unread notifications + */ + public static Iterator getUnreadNotifications( + UINotificationGroup group) + { + return group.getUnreadNotifications(); + } + + /** + * Returns a list of all notification groups. + * + * @return a list of all notification groups + */ + public static Collection getNotificationGroups() + { + return new ArrayList(notificationGroups); + } + + /** + * Notifies interested UINotificationListener that a new + * notification has been received. + * + * @param notification the new notification + */ + private static void fireNotificationEvent(UINotification notification) + { + synchronized (notificationListeners) + { + Iterator listeners + = notificationListeners.iterator(); + + while (listeners.hasNext()) + listeners.next().notificationReceived(notification); + } + } +} diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallHistoryButton.java b/src/net/java/sip/communicator/impl/gui/main/call/CallHistoryButton.java index eb66f3e51..785235684 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/CallHistoryButton.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/CallHistoryButton.java @@ -13,6 +13,7 @@ import javax.swing.*; import net.java.sip.communicator.impl.gui.*; +import net.java.sip.communicator.impl.gui.main.*; import net.java.sip.communicator.impl.gui.main.contactlist.*; import net.java.sip.communicator.impl.gui.utils.*; import net.java.sip.communicator.util.*; @@ -27,7 +28,7 @@ */ public class CallHistoryButton extends SIPCommTextButton - implements MissedCallsListener, + implements UINotificationListener, Skinnable { /** @@ -46,17 +47,10 @@ public class CallHistoryButton private boolean isHistoryVisible = false; /** - * Indicates if this button currently shows the number of missed calls or - * the just the history icon. + * Indicates if this button currently shows the number of unread + * notifications or the just the history icon. */ - private boolean isMissedCallView = false; - - /** - * The tool tip shown when there are missed calls. - */ - private final static String missedCallsToolTip - = GuiActivator.getResources().getI18NString( - "service.gui.MISSED_CALLS_TOOL_TIP"); + private boolean isNotificationsView = false; /** * The tool tip shown by default over the history button. @@ -79,7 +73,7 @@ public CallHistoryButton() { super(""); - CallManager.setMissedCallsListener(this); + UINotificationManager.addNotificationListener(this); this.setPreferredSize(new Dimension(29, 22)); this.setForeground(Color.WHITE); @@ -95,7 +89,7 @@ public CallHistoryButton() { public void actionPerformed(ActionEvent e) { - if (isHistoryVisible && !isMissedCallView) + if (isHistoryVisible && !isNotificationsView) { GuiActivator.getContactList() .setDefaultFilter(TreeContactList.presenceFilter); @@ -109,7 +103,7 @@ public void actionPerformed(ActionEvent e) .setDefaultFilter(TreeContactList.historyFilter); GuiActivator.getContactList().applyDefaultFilter(); - CallManager.clearMissedCalls(); + UINotificationManager.removeAllNotifications(); isHistoryVisible = true; } @@ -122,14 +116,18 @@ public void actionPerformed(ActionEvent e) } /** - * Indicates that missed calls count has changed. - * @param missedCalls the list of missed calls + * Indicates that a new notification is received. + * + * @param notification the notification that was received */ - public void missedCallCountChanged(Collection missedCalls) + public void notificationReceived(UINotification notification) { - if (!missedCalls.isEmpty()) + Collection notificationGroups + = UINotificationManager.getNotificationGroups(); + + if (!isHistoryVisible && notificationGroups.size() > 0) { - setMissedCallsView(missedCalls); + setNotificationView(notificationGroups); } else { @@ -145,7 +143,7 @@ public void missedCallCountChanged(Collection missedCalls) */ private void setHistoryView() { - isMissedCallView = false; + isNotificationsView = false; if (isHistoryVisible) { @@ -161,36 +159,53 @@ private void setHistoryView() } /** - * Sets the missed calls view of this button. - * @param missedCalls the list of missed calls + * Sets the notifications view of this button. + * + * @param notificationGroups the list of unread notification groups */ - private void setMissedCallsView(Collection missedCalls) + private void setNotificationView( + Collection notificationGroups) { - isMissedCallView = true; + int notificationCount = 0; + isNotificationsView = true; this.setBgImage(null); - String tooltipText = "" + missedCallsToolTip + "
"; + Iterator groupsIter + = notificationGroups.iterator(); + + String tooltipText = ""; - int visibleCallCount = 5; - Iterator callsIter = missedCalls.iterator(); - while (callsIter.hasNext() && visibleCallCount > 0) + while (groupsIter.hasNext()) { - MissedCall missedCall = callsIter.next(); - tooltipText += GuiUtils.formatTime(missedCall.getCallTime()) - + " "+ missedCall.getCallName() + "
"; - visibleCallCount--; - - if (visibleCallCount == 0 && callsIter.hasNext()) - tooltipText - += GuiActivator.getResources() + UINotificationGroup group = groupsIter.next(); + + tooltipText += "" + group.getGroupDisplayName() + "
"; + + notificationCount += group.getUnreadNotificationsCount(); + + int visibleNotifsPerGroup = 5; + Iterator notifsIter = group.getUnreadNotifications(); + + while (notifsIter.hasNext() && visibleNotifsPerGroup > 0) + { + UINotification missedCall = notifsIter.next(); + tooltipText += GuiUtils.formatTime(missedCall.getTime()) + + " " + missedCall.getDisplayName() + "
"; + + visibleNotifsPerGroup--; + + if (visibleNotifsPerGroup == 0 && notifsIter.hasNext()) + tooltipText += GuiActivator.getResources() .getI18NString("service.gui.MISSED_CALLS_MORE_TOOL_TIP", - new String[]{ - new Integer(missedCalls.size() - 5).toString()}); + new String[]{ new Integer( + notificationCount - 5).toString()}); + } } + this.setToolTipText(tooltipText + ""); this.setBackground(new Color(200, 0, 0)); - this.setText(new Integer(missedCalls.size()).toString()); + this.setText(new Integer(notificationCount).toString()); } /** diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java b/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java index c86b5ca55..e70692dcc 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java @@ -14,6 +14,8 @@ import net.java.sip.communicator.impl.gui.*; import net.java.sip.communicator.impl.gui.customcontrols.*; +import net.java.sip.communicator.impl.gui.main.*; +import net.java.sip.communicator.impl.gui.main.contactlist.*; import net.java.sip.communicator.impl.gui.utils.*; import net.java.sip.communicator.service.contactlist.*; import net.java.sip.communicator.service.neomedia.device.*; @@ -44,17 +46,6 @@ public class CallManager private static Hashtable activeCalls = new Hashtable(); - /** - * A list of the currently missed calls. Only the names of the first - * participant of each call is stored in the list. - */ - private static Collection missedCalls; - - /** - * Listener notified for changes in missed calls count. - */ - private static MissedCallsListener missedCallsListener; - /** * The property indicating if the user should be warned when starting a * desktop sharing session. @@ -63,6 +54,11 @@ public class CallManager = "net.java.sip.communicator.impl.gui.main" + ".call.SHOW_DESKTOP_SHARING_WARNING"; + /** + * The group of notifications dedicated to missed calls. + */ + private static UINotificationGroup missedCallGroup; + /** * A call listener. */ @@ -100,18 +96,34 @@ public void incomingCallReceived(CallEvent event) @Override public void callStateChanged(CallChangeEvent evt) { - if (evt.getNewValue().equals(CallState.CALL_ENDED) - && evt.getOldValue() - .equals(CallState.CALL_INITIALIZATION)) + if (evt.getNewValue().equals(CallState.CALL_ENDED)) { - // if call was answered elsewhere, don't add it - // to missed calls - if(evt.getCause() == null - || (evt.getCause().getReasonCode() != - CallPeerChangeEvent.NORMAL_CALL_CLEARING)) - addMissedCall(new MissedCall(peerName, callDate)); - - evt.getSourceCall().removeCallChangeListener(this); + if (evt.getOldValue() + .equals(CallState.CALL_INITIALIZATION)) + { + // if call was answered elsewhere, don't add it + // to missed calls + if(evt.getCause() == null + || (evt.getCause().getReasonCode() != + CallPeerChangeEvent.NORMAL_CALL_CLEARING)) + { + addMissedCallNotification(peerName, callDate); + } + + evt.getSourceCall().removeCallChangeListener(this); + } + + // If we're currently in the call history view refresh + // the view. + TreeContactList contactList + = GuiActivator.getContactList(); + + if (contactList.getCurrentFilter() + .equals(TreeContactList.historyFilter)) + { + contactList.applyFilter( + TreeContactList.historyFilter); + } } } }); @@ -730,51 +742,6 @@ public static CallDialog getActiveCallDialog(Call call) return activeCalls.get(call); } - /** - * Sets the given MissedCallsListener that would be notified on - * any changes in missed calls count. - * @param l the listener to set - */ - public static void setMissedCallsListener(MissedCallsListener l) - { - missedCallsListener = l; - } - - /** - * Adds a missed call. - * @param missedCall the missed call to add to the list of missed calls - */ - private static void addMissedCall(MissedCall missedCall) - { - if (missedCalls == null) - { - missedCalls = new LinkedList(); - } - - missedCalls.add(missedCall); - fireMissedCallCountChangeEvent(missedCalls); - } - - /** - * Clears the count of missed calls. Sets it to 0. - */ - public static void clearMissedCalls() - { - missedCalls = null; - } - - /** - * Notifies interested MissedCallListener that the count has - * changed. - * @param missedCalls the new missed calls - */ - private static void fireMissedCallCountChangeEvent( - Collection missedCalls) - { - if (missedCallsListener != null) - missedCallsListener.missedCallCountChanged(missedCalls); - } - /** * Returns the image corresponding to the given peer. * @@ -854,6 +821,27 @@ public void callStateChanged(CallChangeEvent evt) } } + /** + * Adds a missed call notification. + * + * @param peerName the name of the peer + * @param callDate the date of the call + */ + private static void addMissedCallNotification( String peerName, + Date callDate) + { + if (missedCallGroup == null) + missedCallGroup + = new UINotificationGroup("MissedCalls", + GuiActivator.getResources().getI18NString( + "service.gui.MISSED_CALLS_TOOL_TIP")); + + UINotificationManager.addNotification(new UINotification( + peerName, + callDate, + missedCallGroup)); + } + /** * Creates a call from a given Contact or a given String. */ diff --git a/src/net/java/sip/communicator/impl/gui/main/call/MissedCall.java b/src/net/java/sip/communicator/impl/gui/main/call/MissedCall.java deleted file mode 100644 index be689633d..000000000 --- a/src/net/java/sip/communicator/impl/gui/main/call/MissedCall.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. - */ -package net.java.sip.communicator.impl.gui.main.call; - -import java.util.*; - -/** - * The MissedCall class wraps a missed call in order to provide - * information about the call later. - * - * @author Yana Stamcheva - */ -public class MissedCall -{ - private final String callName; - - private final Date callTime; - - /** - * Creates an instance of MissedCall by specifying the call name - * and time. - * @param callName the name associated to this call - * @param callTime the time of the missed call - */ - public MissedCall(String callName, Date callTime) - { - this.callName = callName; - this.callTime = callTime; - } - - /** - * Returns the name associated with this call. - * @return the name associated with this call - */ - public String getCallName() - { - return callName; - } - - /** - * Returns the time the call was made. - * @return the time the call was made - */ - public Date getCallTime() - { - return callTime; - } -} diff --git a/src/net/java/sip/communicator/impl/gui/main/call/MissedCallsListener.java b/src/net/java/sip/communicator/impl/gui/main/call/MissedCallsListener.java deleted file mode 100644 index 843d2ab4c..000000000 --- a/src/net/java/sip/communicator/impl/gui/main/call/MissedCallsListener.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. - */ -package net.java.sip.communicator.impl.gui.main.call; - -import java.util.*; - -/** - * The MissedCallsListener listens for changes in the missed calls - * count. It is notified each time when a missed calls is registered by the - * CallManager. - * - * @author Yana Stamcheva - */ -public interface MissedCallsListener -{ - /** - * Indicates the missed calls count has changed. - * @param missedCalls the new missed calls list - */ - public void missedCallCountChanged(Collection missedCalls); -} diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/CallHistoryFilter.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/CallHistoryFilter.java index 2c8f450ea..267d4c512 100644 --- a/src/net/java/sip/communicator/impl/gui/main/contactlist/CallHistoryFilter.java +++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/CallHistoryFilter.java @@ -10,6 +10,7 @@ import net.java.sip.communicator.impl.gui.*; import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*; +import net.java.sip.communicator.impl.gui.main.contactlist.notifsource.*; import net.java.sip.communicator.service.contactsource.*; /** @@ -22,14 +23,23 @@ public class CallHistoryFilter { /** * Applies this filter and stores the result in the given treeModel. + * * @param filterQuery the FilterQuery that tracks the results of * this filtering */ public void applyFilter(FilterQuery filterQuery) { + // First add all notifications. + NotificationContactSource notificationSource + = TreeContactList.getNotificationContactSource(); + + if (notificationSource != null) + addMatching(notificationSource); + Collection contactSources = TreeContactList.getContactSources(); + // Then add Call history contact source. for (ExternalContactSource contactSource : contactSources) { ContactSourceService sourceService @@ -57,6 +67,7 @@ public void applyFilter(FilterQuery filterQuery) /** * Indicates if the given uiContact is matching this filter. + * * @param uiContact the UIContact to check for match * @return true if the given uiContact is matching this * filter, false otherwise @@ -73,21 +84,33 @@ public boolean isMatching(UIContact uiContact) .equals(ContactSourceService.CALL_HISTORY))) return true; } + else if (uiContact instanceof NotificationContact) + { + return true; + } + return false; } /** * No group could match this filter. + * * @param uiGroup the UIGroup to check for match * @return false to indicate that no group could match this filter */ public boolean isMatching(UIGroup uiGroup) { + if (uiGroup instanceof NotificationGroup) + { + return true; + } + return false; } /** * Adds matching sourceContacts to the result tree model. + * * @param sourceContacts the list of SourceContacts to add * @param uiSource the ExternalContactSource, which contacts * we're adding @@ -105,4 +128,31 @@ private void addMatching( List sourceContacts, false); } } + + /** + * Adds matching notification contacts to the result tree model. + * + * @param notifSource + */ + private void addMatching(NotificationContactSource notifSource) + { + Iterator notifGroups + = notifSource.getNotificationGroups(); + + while (notifGroups.hasNext()) + { + UIGroup uiGroup = notifGroups.next(); + + Iterator notfications + = notifSource.getNotifications(uiGroup); + + while (notfications.hasNext()) + { + GuiActivator.getContactList() + .addContact(notfications.next(), + uiGroup, + false); + } + } + } } diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeCellRenderer.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeCellRenderer.java index 4b1a6cefa..3f02971a1 100644 --- a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeCellRenderer.java +++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeCellRenderer.java @@ -176,7 +176,7 @@ public ContactListTreeCellRenderer() loadSkin(); - this.setOpaque(false); + this.setOpaque(true); this.nameLabel.setOpaque(false); this.displayDetailsLabel.setFont(getFont().deriveFont(9f)); @@ -299,6 +299,26 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, DefaultTreeContactList contactList = (DefaultTreeContactList) tree; + // Set background color. + if (contactList instanceof TreeContactList) + { + ContactListFilter filter + = ((TreeContactList) contactList).getCurrentFilter(); + + if (filter != null + && filter.equals(TreeContactList.historyFilter) + && value instanceof ContactNode + && row%2 == 0) + { + setBackground(Constants.CALL_HISTORY_EVEN_ROW_COLOR); + } + else + { + setBackground(Color.WHITE); + } + } + + // Make appropriate adjustments for contact nodes and group nodes. if (value instanceof ContactNode) { UIContact contact diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/TreeContactList.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/TreeContactList.java index 3801677c9..4df88f8cf 100644 --- a/src/net/java/sip/communicator/impl/gui/main/contactlist/TreeContactList.java +++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/TreeContactList.java @@ -14,12 +14,10 @@ import javax.swing.event.*; import javax.swing.tree.*; -import org.osgi.framework.*; - import net.java.sip.communicator.impl.gui.*; import net.java.sip.communicator.impl.gui.main.*; -import net.java.sip.communicator.impl.gui.main.MainFrame.*; import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*; +import net.java.sip.communicator.impl.gui.main.contactlist.notifsource.*; import net.java.sip.communicator.impl.gui.utils.*; import net.java.sip.communicator.service.contactlist.*; import net.java.sip.communicator.service.contactlist.event.*; @@ -29,6 +27,8 @@ import net.java.sip.communicator.util.*; import net.java.sip.communicator.util.swing.*; +import org.osgi.framework.*; + /** * The TreeContactList is a contact list based on JTree. * @@ -115,6 +115,8 @@ public class TreeContactList private static final Collection contactSources = new LinkedList(); + private static NotificationContactSource notificationSource; + private FilterQuery currentFilterQuery; private FilterThread filterThread; @@ -627,7 +629,9 @@ public void queryStatusChanged(ContactQueryStatusEvent event) if (eventType == ContactQueryStatusEvent.QUERY_ERROR) { - //TODO: Show the error to the user?? + if (logger.isInfoEnabled()) + logger.info("Contact query error occured: " + + event.getQuerySource()); } event.getQuerySource().removeContactQueryListener(this); } @@ -758,7 +762,9 @@ public void run() if (isSorted) groupNode = parentNode.sortedAddContactGroup(group); else + { groupNode = parentNode.addContactGroup(group); + } } } @@ -875,6 +881,29 @@ public void run() } } + /** + * Indicates that the information corresponding to the given + * contact has changed. + * + * @param contact the contact that has changed + */ + public void refreshContact(final UIContact contact) + { + if (!SwingUtilities.isEventDispatchThread()) + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + refreshContact(contact); + } + }); + return; + } + + treeModel.nodeChanged(contact.getContactNode()); + } + /** * Adds the given group to this list. * @param group the UIGroup to add @@ -1715,6 +1744,19 @@ public static Collection getContactSources() return contactSources; } + /** + * Returns the notification contact source. + * + * @return the notification contact source + */ + public static NotificationContactSource getNotificationContactSource() + { + if (notificationSource == null) + notificationSource = new NotificationContactSource(); + + return notificationSource; + } + /** * Returns the ExternalContactSource corresponding to the given * ContactSourceService. diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/UIContact.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/UIContact.java index 6f9b04b34..1e409b80a 100644 --- a/src/net/java/sip/communicator/impl/gui/main/contactlist/UIContact.java +++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/UIContact.java @@ -24,12 +24,14 @@ public interface UIContact { /** * Returns the descriptor of this contact. + * * @return the descriptor of this contact */ public Object getDescriptor(); /** * Returns the display name of this contact. + * * @return the display name of this contact */ public String getDisplayName(); @@ -37,18 +39,21 @@ public interface UIContact /** * Returns the display details of this contact. These would be shown * whenever the contact is selected. + * * @return the display details of this contact */ public String getDisplayDetails(); /** * Returns the index of this contact in its source. + * * @return the source index */ public int getSourceIndex(); /** * Returns the avatar of this contact. + * * @param isSelected indicates if the contact is selected * @param width the width of the avatar * @param height the height of the avatar @@ -59,6 +64,7 @@ public interface UIContact /** * Returns the status icon of this contact or null if no status is * available. + * * @return the status icon of this contact or null if no status is * available */ @@ -67,18 +73,21 @@ public interface UIContact /** * Creates a tool tip for this contact. If such tooltip is * provided it would be shown on mouse over over this UIContact. + * * @return the tool tip for this contact descriptor */ public ExtendedTooltip getToolTip(); /** * Returns the right button menu component. + * * @return the right button menu component */ public JPopupMenu getRightButtonMenu(); /** * Returns the parent group. + * * @return the parent group */ public UIGroup getParentGroup(); @@ -86,6 +95,7 @@ public interface UIContact /** * Sets the given UIGroup to be the parent group of this * UIContact. + * * @param parentGroup the parent UIGroup of this contact */ public void setParentGroup(UIGroup parentGroup); @@ -93,6 +103,7 @@ public interface UIContact /** * Returns an Iterator over a list of the search strings of this * contact. + * * @return an Iterator over a list of the search strings of this * contact */ @@ -101,6 +112,7 @@ public interface UIContact /** * Returns the corresponding ContactNode. The ContactNode * is the real node that is stored in the contact list component data model. + * * @return the corresponding ContactNode */ public ContactNode getContactNode(); @@ -108,6 +120,7 @@ public interface UIContact /** * Sets the given contactNode. The ContactNode * is the real node that is stored in the contact list component data model. + * * @param contactNode the ContactNode that corresponds to this * UIGroup */ @@ -116,6 +129,7 @@ public interface UIContact /** * Returns the default ContactDetail to use for any operations * depending to the given OperationSet class. + * * @param opSetClass the OperationSet class we're interested in * @return the default ContactDetail to use for any operations * depending to the given OperationSet class @@ -126,6 +140,7 @@ public UIContactDetail getDefaultContactDetail( /** * Returns a list of all UIContactDetails corresponding to the * given OperationSet class. + * * @param opSetClass the OperationSet class we're looking for * @return a list of all UIContactDetails corresponding to the * given OperationSet class diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/UIGroup.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/UIGroup.java index baf57e277..825d873f3 100644 --- a/src/net/java/sip/communicator/impl/gui/main/contactlist/UIGroup.java +++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/UIGroup.java @@ -18,6 +18,7 @@ public interface UIGroup /** * Returns the descriptor of the group. This would be the underlying object * that should provide all other necessary information for the group. + * * @return the descriptor of the group */ public Object getDescriptor(); @@ -25,6 +26,7 @@ public interface UIGroup /** * The display name of the group. The display name is the name to be shown * in the contact list group row. + * * @return the display name of the group */ public String getDisplayName(); @@ -32,18 +34,21 @@ public interface UIGroup /** * Returns the index of this group in its source. In other words this is * the descriptor index. + * * @return the index of this group in its source */ public int getSourceIndex(); /** * Returns the parent group. + * * @return the parent group */ public UIGroup getParentGroup(); /** * Indicates if the group is collapsed or expanded. + * * @return true to indicate that the group is collapsed, * false to indicate that it's expanded */ @@ -51,18 +56,21 @@ public interface UIGroup /** * Returns the count of online child contacts. + * * @return the count of online child contacts */ public int countOnlineChildContacts(); /** * Returns the child contacts count. + * * @return child contacts count */ public int countChildContacts(); /** * Returns the identifier of this group. + * * @return the identifier of this group */ public String getId(); @@ -70,12 +78,14 @@ public interface UIGroup /** * Returns the GroupNode corresponding to this UIGroup. * The is the actual node used in the contact list component data model. + * * @return the GroupNode corresponding to this UIGroup */ public GroupNode getGroupNode(); /** * Sets the GroupNode corresponding to this UIGroup. + * * @param groupNode the GroupNode to set. The is the actual * node used in the contact list component data model. */ @@ -83,6 +93,7 @@ public interface UIGroup /** * Returns the right button menu for this group. + * * @return the right button menu component for this group */ public JPopupMenu getRightButtonMenu(); diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/ExternalContactSource.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/ExternalContactSource.java index ad09b93ee..87cac63ce 100644 --- a/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/ExternalContactSource.java +++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/ExternalContactSource.java @@ -29,18 +29,20 @@ public class ExternalContactSource /** * Creates an ExternalContactSource based on the given * ContactSourceService. + * * @param contactSource the ContactSourceService, on which this * ExternalContactSource is based */ public ExternalContactSource(ContactSourceService contactSource) { this.contactSource = contactSource; - sourceUIGroup - = new SourceUIGroup(contactSource.getDisplayName()); + + sourceUIGroup = new SourceUIGroup(contactSource.getDisplayName()); } /** * Returns the corresponding ContactSourceService. + * * @return the corresponding ContactSourceService */ public ContactSourceService getContactSourceService() @@ -51,6 +53,7 @@ public ContactSourceService getContactSourceService() /** * Returns the UI group for this contact source. There's only one group * descriptor per external source. + * * @return the group descriptor */ public UIGroup getUIGroup() @@ -61,6 +64,7 @@ public UIGroup getUIGroup() /** * Returns the UIContact corresponding to the given * sourceContact. + * * @param sourceContact the SourceContact, for which we search a * corresponding UIContact * @return the UIContact corresponding to the given diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/notifsource/NotificationContact.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/notifsource/NotificationContact.java new file mode 100644 index 000000000..be1249591 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/notifsource/NotificationContact.java @@ -0,0 +1,456 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.gui.main.contactlist.notifsource; + +import java.beans.*; +import java.util.*; + +import javax.swing.*; + +import net.java.sip.communicator.impl.gui.*; +import net.java.sip.communicator.impl.gui.main.contactlist.*; +import net.java.sip.communicator.impl.gui.utils.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.OperationSetMessageWaiting.MessageType; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.service.resources.*; + +/** + * The NotificationContact represents a notification entry shown in + * the contact list history view. It could represent a voice message entry, + * email notification or something else. + * + * @author Yana Stamcheva + */ +public class NotificationContact + implements UIContact, + RegistrationStateChangeListener, + ProviderPresenceStatusListener +{ + /** + * The tip explaining to the user how to hear its voice messages. + */ + private static final String VOICEMAIL_TIP + = GuiActivator.getResources() + .getI18NString("service.gui.VOICEMAIL_TIP"); + + /** + * The parent contact list group. + */ + private NotificationGroup parentGroup; + + /** + * The corresponding protocol provider. + */ + private final ProtocolProviderService protocolProvider; + + /** + * The corresponding ContactNode in the contact list component + * data model. + */ + private ContactNode contactNode; + + /** + * The notification detail. + */ + private UIContactDetail notificationDetail; + + /** + * The count of unread messages attached to this notification. + */ + private int unreadMessageCount = 0; + + /** + * The count of read messages attached to this notification. + */ + private int readMessageCount = 0; + + /** + * Creates an instance of NotificationContact by specifying the + * parent group and the corresponding ProtocolProviderService. + * + * @param group the parent group + * @param protocolProvider the corresponding protocol provider + */ + public NotificationContact( NotificationGroup group, + ProtocolProviderService protocolProvider) + { + this.parentGroup = group; + this.protocolProvider = protocolProvider; + + protocolProvider.addRegistrationStateChangeListener(this); + + OperationSetPresence presenceOpSet + = protocolProvider.getOperationSet(OperationSetPresence.class); + + if (presenceOpSet != null) + presenceOpSet.addProviderPresenceStatusListener(this); + } + + /** + * Returns the descriptor of this contact. + * + * @return the descriptor of this contact + */ + public Object getDescriptor() + { + return protocolProvider; + } + + /** + * Returns the display name of this contact. + * + * @return the display name of this contact + */ + public String getDisplayName() + { + return protocolProvider.getAccountID().getAccountAddress(); + } + + /** + * Returns the display details of this contact. These would be shown + * whenever the contact is selected. In the NotificationContact + * these contain information about unread and read messages. + * + * @return the display details of this contact + */ + public String getDisplayDetails() + { + String displayDetails; + + ResourceManagementService resources = GuiActivator.getResources(); + + if (unreadMessageCount > 0 && readMessageCount > 0) + { + displayDetails = resources.getI18NString( + "service.gui.VOICEMAIL_NEW_OLD_RECEIVED", + new String[]{ Integer.toString(unreadMessageCount), + Integer.toString(readMessageCount)}); + } + else if (unreadMessageCount > 0) + { + displayDetails = resources.getI18NString( + "service.gui.VOICEMAIL_NEW_RECEIVED", + new String[]{ Integer.toString(unreadMessageCount)}); + } + else if (readMessageCount > 0) + { + displayDetails = resources.getI18NString( + "service.gui.VOICEMAIL_OLD_RECEIVED", + new String[]{ Integer.toString(readMessageCount)}); + } + else + { + displayDetails = resources.getI18NString( + "service.gui.VOICEMAIL_NO_MESSAGES"); + } + + return displayDetails; + } + + /** + * Returns the index of this contact in its source. + * + * @return the source index + */ + public int getSourceIndex() + { + return 0; + } + + /** + * Returns the icon indicating that this is a notification contact. + * + * @param isSelected indicates if the contact is selected + * @param width the width of the avatar + * @param height the height of the avatar + * @return the avatar of this contact + */ + public ImageIcon getAvatar(boolean isSelected, int width, int height) + { + ImageIcon avatarIcon = null; + if (parentGroup.getMessageType().equals(MessageType.VOICE)) + { + avatarIcon = GuiActivator.getResources().getImage( + "service.gui.icons.VOICEMAIL"); + } + + return avatarIcon; + } + + /** + * Returns the status icon of this contact or null if no status is + * available. + * + * @return the status icon of this contact or null if no status is + * available + */ + public ImageIcon getStatusIcon() + { + OperationSetPresence presence = protocolProvider + .getOperationSet(OperationSetPresence.class); + + if (presence != null) + { + return new ImageIcon( + Constants.getStatusIcon(presence.getPresenceStatus())); + } + else if (protocolProvider.isRegistered()) + { + return new ImageIcon( + Constants.getStatusIcon(Constants.ONLINE_STATUS)); + } + + return new ImageIcon( + Constants.getStatusIcon(Constants.OFFLINE_STATUS)); + } + + /** + * Creates a tool tip for this contact. If such tooltip is + * provided it would be shown on mouse over over this UIContact. + * + * @return the tool tip for this contact descriptor + */ + /** + * Returns the tool tip opened on mouse over. + * @return the tool tip opened on mouse over + */ + public ExtendedTooltip getToolTip() + { + ExtendedTooltip tip = new ExtendedTooltip(true); + + ImageIcon avatarImage = getAvatar(true, 64, 64); + + if (avatarImage != null) + tip.setImage(avatarImage); + + tip.setTitle(getDisplayName()); + + tip.addLine(null, getDisplayDetails()); + + tip.setBottomText(VOICEMAIL_TIP); + + return tip; + } + + /** + * Returns null to indicate that no right button menu is provided for this + * contact. + * + * @return null + */ + public JPopupMenu getRightButtonMenu() + { + return null; + } + + /** + * Returns the parent group. + * + * @return the parent group + */ + public UIGroup getParentGroup() + { + return parentGroup; + } + + /** + * Sets the given UIGroup to be the parent group of this + * UIContact. + * + * @param parentGroup the parent UIGroup of this contact + */ + public void setParentGroup(UIGroup parentGroup) + { + if (!(parentGroup instanceof NotificationGroup)) + return; + + this.parentGroup = (NotificationGroup) parentGroup; + } + + /** + * No search strings are provided for this contact. + * + * @return null + */ + public Iterator getSearchStrings() + { + return null; + } + + /** + * Returns the corresponding ContactNode. The ContactNode + * is the real node that is stored in the contact list component data model. + * + * @return the corresponding ContactNode + */ + public ContactNode getContactNode() + { + return contactNode; + } + + /** + * Sets the given contactNode. The ContactNode + * is the real node that is stored in the contact list component data model. + * + * @param contactNode the ContactNode that corresponds to this + * UIGroup + */ + public void setContactNode(ContactNode contactNode) + { + this.contactNode = contactNode; + } + + /** + * Returns the default ContactDetail to use for any operations + * depending to the given OperationSet class. + * + * @param opSetClass the OperationSet class we're interested in + * @return the default ContactDetail to use for any operations + * depending to the given OperationSet class + */ + public UIContactDetail getDefaultContactDetail( + Class opSetClass) + { + if (opSetClass.equals(OperationSetBasicTelephony.class)) + return notificationDetail; + + return null; + } + + /** + * Returns a list of UIContactDetails supporting the given + * OperationSet class. + * + * @param opSetClass the OperationSet class we're interested in + * @return a list of UIContactDetails supporting the given + * OperationSet class + */ + public List getContactDetailsForOperationSet( + Class opSetClass) + { + List resultList = new LinkedList(); + + if (opSetClass.equals(OperationSetBasicTelephony.class)) + resultList.add(notificationDetail); + + return resultList; + } + + /** + * Sets the corresponding message account. + * + * @param messageAccount the message account corresponding to the contained + * notification + */ + public void setMessageAccount(String messageAccount) + { + notificationDetail + = new MessageWaitingDetail(protocolProvider, messageAccount); + } + + /** + * Sets the number of unread messages, this notification is about. + * + * @param count the number of unread messages, this notification is about + */ + public void setUnreadMessageCount(int count) + { + this.unreadMessageCount = count; + } + + /** + * Returns the number of unread messages, this notification is about. + * + * @return the number of unread messages, this notification is about + */ + public int getUnreadMessageCount() + { + return unreadMessageCount; + } + + /** + * Sets the number of read messages, this notification is about. + * + * @param count the number of read messages, this notification is about + */ + public void setReadMessageCount(int count) + { + this.readMessageCount = count; + } + + /** + * The implementation of the UIContactDetail interface for the + * external source ContactDetails. + */ + private class MessageWaitingDetail + extends UIContactDetail + { + /** + * Creates an instance of SourceContactDetail by specifying + * the underlying detail and the OperationSet class + * for it. + * + * @param protocolProvider the protocol provider corresponding to this + * detail + * @param messageAccount the message account corresponding to this + * detail + */ + public MessageWaitingDetail(ProtocolProviderService protocolProvider, + String messageAccount) + { + super( messageAccount, + messageAccount, + protocolProvider, + protocolProvider.getProtocolName()); + } + + /** + * Returns null to indicate that this detail doesn't support presence. + * @return null + */ + public PresenceStatus getPresenceStatus() + { + return null; + } + } + + /** + * Refresh the notification contact corresponding the the attached provider + * in order to better reflect its state. + * + * @param evt the ProviderPresenceStatusChangeEvent that has + * notified us of the state change + */ + public void registrationStateChanged(RegistrationStateChangeEvent evt) + { + RegistrationState newState = evt.getNewState(); + if (newState.equals(RegistrationState.UNREGISTERED) + || newState.equals(RegistrationState.REGISTERED)) + { + TreeContactList contactList = GuiActivator.getContactList(); + + contactList.refreshContact(this); + } + } + + /** + * Refresh the notification contact corresponding the the attached provider + * in order to better reflect its status. + * + * @param evt the ProviderPresenceStatusChangeEvent that has + * notified us of the status change + */ + public void providerStatusChanged(ProviderPresenceStatusChangeEvent evt) + { + TreeContactList contactList = GuiActivator.getContactList(); + + contactList.refreshContact(this); + } + + public void providerStatusMessageChanged(PropertyChangeEvent evt) {} +} diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/notifsource/NotificationContactSource.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/notifsource/NotificationContactSource.java new file mode 100644 index 000000000..9f0f2a21d --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/notifsource/NotificationContactSource.java @@ -0,0 +1,86 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.gui.main.contactlist.notifsource; + +import java.util.*; + +import net.java.sip.communicator.impl.gui.*; +import net.java.sip.communicator.impl.gui.main.contactlist.*; +import net.java.sip.communicator.service.protocol.OperationSetMessageWaiting.MessageType; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.util.*; + +/** + * The NotificationContactSource represents a contact source that would + * listen for message waiting notifications and would display them in the + * history view of the contact list. + * + * @author Yana Stamcheva + */ +public class NotificationContactSource + implements MessageWaitingListener +{ + /** + * A mapping attaching to each NotificationGroup the + * corresponding MessageType, for which notifications + * are received. + */ + private final Hashtable groups + = new Hashtable(); + + /** + * Adds the received waiting message to the corresponding group and contact. + * Also adds it the UINotificationManager that would take care of + * notifying the user. + * + * @param evt the notification event. + */ + public void messageWaitingNotify(MessageWaitingEvent evt) + { + MessageType type = evt.getMessageType(); + + NotificationGroup group = groups.get(type); + + if (group == null) + { + group = new NotificationGroup(type); + groups.put(type, group); + } + + group.messageWaitingNotify(evt); + } + + /** + * Returns an Iterator over a list of all notification groups + * contained in this source. + * + * @return an Iterator over a list of all notification groups + * contained in this source + */ + public Iterator getNotificationGroups() + { + return groups.values().iterator(); + } + + /** + * Returns an Iterator over a list of all notification contacts + * contained in the given group. + * + * @param group the group, which notification contacts we're looking for + * @return an Iterator over a list of all notification contacts + * contained in the given group + */ + public Iterator getNotifications(UIGroup group) + { + if (!(group instanceof NotificationGroup)) + return null; + + NotificationGroup notifGroup = (NotificationGroup) group; + + return notifGroup.getNotifications(); + } +} diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/notifsource/NotificationGroup.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/notifsource/NotificationGroup.java new file mode 100644 index 000000000..a4b257efc --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/notifsource/NotificationGroup.java @@ -0,0 +1,262 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.gui.main.contactlist.notifsource; + +import java.util.*; + +import javax.swing.*; + +import net.java.sip.communicator.impl.gui.*; +import net.java.sip.communicator.impl.gui.main.*; +import net.java.sip.communicator.impl.gui.main.contactlist.*; +import net.java.sip.communicator.service.protocol.OperationSetMessageWaiting.MessageType; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; + +/** + * The NotificationGroup represents a group of notification entries + * shown in the contact list history view. It could represent the voice + * messages,.emails, etc. + * + * @author Yana Stamcheva + */ +public class NotificationGroup + implements UIGroup +{ + /** + * The type of the notification message, identifying this group. + */ + private final MessageType type; + + /** + * The corresponding group node in the contact list component. + */ + private GroupNode groupNode; + + /** + * A mapping attaching to each NotificationContact the + * corresponding ProtocolProviderService, for which notifications + * are received. + */ + private final Hashtable + contacts + = new Hashtable(); + + /** + * The group of UI notifications. + */ + private static UINotificationGroup uiNotificationGroup; + + /** + * Creates an instance of NotificationGroup by specifying the + * message type. + * + * @param type the type of messages that this group would contain + */ + public NotificationGroup(MessageType type) + { + this.type = type; + } + + /** + * Returns the descriptor of the group. This would be the underlying object + * that should provide all other necessary information for the group. + * + * @return the descriptor of the group + */ + public Object getDescriptor() + { + return type; + } + + /** + * The display name of the group. The display name is the name to be shown + * in the contact list group row. + * + * @return the display name of the group + */ + public String getDisplayName() + { + String displayName; + if (type.equals(MessageType.VOICE)) + displayName = GuiActivator.getResources() + .getI18NString("service.gui.VOICEMAIL_TITLE"); + else + displayName = type.toString(); + + return displayName; + } + + /** + * Returns the index of this group in its source. In other words this is + * the descriptor index. + * + * @return the index of this group in its source + */ + public int getSourceIndex() + { + return 0; + } + + /** + * Returns null to indicate that the parent group is the root group. + * + * @return null + */ + public UIGroup getParentGroup() + { + return null; + } + + /** + * Returns false to indicate that this group is never collapsed. + * + * @return false + */ + public boolean isGroupCollapsed() + { + return false; + } + + /** + * Returns the count of online child contacts. + * + * @return the count of online child contacts + */ + public int countOnlineChildContacts() + { + return contacts.size(); + } + + /** + * Returns the child contacts count. + * + * @return child contacts count + */ + public int countChildContacts() + { + return contacts.size(); + } + + /** + * Returns the identifier of this group. + * + * @return the identifier of this group + */ + public String getId() + { + return null; + } + + /** + * Returns the GroupNode corresponding to this UIGroup. + * The is the actual node used in the contact list component data model. + * + * @return the GroupNode corresponding to this UIGroup + */ + public GroupNode getGroupNode() + { + return groupNode; + } + + /** + * Sets the GroupNode corresponding to this UIGroup. + * + * @param groupNode the GroupNode to set. The is the actual + * node used in the contact list component data model. + */ + public void setGroupNode(GroupNode groupNode) + { + this.groupNode = groupNode; + } + + /** + * Returns null to indicate that there's no right button menu provided for + * this group. + * + * @return null + */ + public JPopupMenu getRightButtonMenu() + { + return null; + } + + /** + * Returns an Iterator over a list of all notification contacts + * contained in this group. + * + * @return an Iterator over a list of all notification contacts + * contained in this group + */ + public Iterator getNotifications() + { + return contacts.values().iterator(); + } + + /** + * Returns the message type indicating the identity of this group. + * + * @return the message type corresponding to this group + */ + public MessageType getMessageType() + { + return type; + } + + /** + * Creates all necessary notification contacts coming from the given + * MessageWaitingEvent. + * + * @param event the MessageWaitingEvent that notified us + */ + public void messageWaitingNotify(MessageWaitingEvent event) + { + ProtocolProviderService protocolProvider = event.getSourceProvider(); + + NotificationContact contact = contacts.get(protocolProvider); + + TreeContactList contactList = GuiActivator.getContactList(); + + boolean isNew = false; + if (contact == null) + { + contact = new NotificationContact(this, protocolProvider); + contacts.put(protocolProvider, contact); + + isNew = true; + } + + contact.setMessageAccount(event.getAccount()); + contact.setUnreadMessageCount(event.getUnreadMessages()); + contact.setReadMessageCount(event.getReadMessages()); + + if (contactList.getCurrentFilter().isMatching(contact)) + { + if (isNew) + contactList.addContact(contact, this, true); + else + contactList.refreshContact(contact); + } + + if (contact.getUnreadMessageCount() > 0) + { + if (uiNotificationGroup == null) + uiNotificationGroup + = new UINotificationGroup( + getDisplayName(), + GuiActivator.getResources() + .getI18NString("service.gui.VOICEMAIL_TOOLTIP")); + + UINotificationManager.addNotification( + new UINotification( + contact.getDisplayName() + + " : " + contact.getDisplayDetails() , + new Date(), + uiNotificationGroup)); + } + } +} diff --git a/src/net/java/sip/communicator/impl/gui/utils/Constants.java b/src/net/java/sip/communicator/impl/gui/utils/Constants.java index 6f3a1c873..4a3786197 100755 --- a/src/net/java/sip/communicator/impl/gui/utils/Constants.java +++ b/src/net/java/sip/communicator/impl/gui/utils/Constants.java @@ -76,20 +76,11 @@ public class Constants * ====================================================================== */ /** - * The color used to paint the background of an incoming call history - * record. + * Background color for even records in call history. */ - public static Color HISTORY_IN_CALL_COLOR + public static Color CALL_HISTORY_EVEN_ROW_COLOR = new Color(GuiActivator.getResources(). - getColor("service.gui.HISTORY_INCOMING_CALL_BACKGROUND")); - - /** - * The color used to paint the background of an outgoing call history - * record. - */ - public static Color HISTORY_OUT_CALL_COLOR - = new Color(GuiActivator.getResources(). - getColor("service.gui.HISTORY_OUTGOING_CALL_BACKGROUND")); + getColor("service.gui.CALL_HISTORY_EVEN_ROW_COLOR")); /** * The end color used to paint a gradient selected background of some @@ -414,13 +405,9 @@ public static void loadSimpleStyle(StyleSheet styleSheet, Font defaultFont) */ public static void reload() { - HISTORY_IN_CALL_COLOR - = new Color(GuiActivator.getResources(). - getColor("service.gui.HISTORY_INCOMING_CALL_BACKGROUND")); - - HISTORY_OUT_CALL_COLOR + CALL_HISTORY_EVEN_ROW_COLOR = new Color(GuiActivator.getResources(). - getColor("service.gui.HISTORY_OUTGOING_CALL_BACKGROUND")); + getColor("service.gui.CALL_HISTORY_EVEN_ROW_COLOR")); SELECTED_COLOR = new Color(GuiActivator.getResources(). diff --git a/src/net/java/sip/communicator/service/protocol/OperationSetMessageWaiting.java b/src/net/java/sip/communicator/service/protocol/OperationSetMessageWaiting.java index bfd2a2963..3e1f39d83 100644 --- a/src/net/java/sip/communicator/service/protocol/OperationSetMessageWaiting.java +++ b/src/net/java/sip/communicator/service/protocol/OperationSetMessageWaiting.java @@ -43,7 +43,8 @@ private MessageType(String type) } /** - * Returns the type ot the message type enum element. + * Returns the type of the message type enum element. + * * @return the message type. */ public String toString() @@ -53,6 +54,7 @@ public String toString() /** * Returns MessageType by its type name. + * * @param type the type. * @return the corresponding MessageType. */ diff --git a/src/net/java/sip/communicator/service/protocol/event/MessageWaitingEvent.java b/src/net/java/sip/communicator/service/protocol/event/MessageWaitingEvent.java index a56d9d12e..bcadea22f 100644 --- a/src/net/java/sip/communicator/service/protocol/event/MessageWaitingEvent.java +++ b/src/net/java/sip/communicator/service/protocol/event/MessageWaitingEvent.java @@ -81,6 +81,16 @@ public MessageWaitingEvent( this.readUrgentMessages = readUrgentMessages; } + /** + * Returns the ProtocolProviderService which originated this event. + * + * @return the source ProtocolProviderService + */ + public ProtocolProviderService getSourceProvider() + { + return (ProtocolProviderService) getSource(); + } + /** * The URI we can use to reach messages from provider that is firing * the event. diff --git a/src/net/java/sip/communicator/service/protocol/event/MessageWaitingListener.java b/src/net/java/sip/communicator/service/protocol/event/MessageWaitingListener.java index 0a2c0ef3f..0995bd2c0 100644 --- a/src/net/java/sip/communicator/service/protocol/event/MessageWaitingListener.java +++ b/src/net/java/sip/communicator/service/protocol/event/MessageWaitingListener.java @@ -14,7 +14,8 @@ public interface MessageWaitingListener { /** - * Notifies for new messages that are waiting. + * Notifies for new messages that are waiting. + * * @param evt the notification event. */ public void messageWaitingNotify(MessageWaitingEvent evt);