/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.java.sip.communicator.impl.gui.main.call;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.chat.*;
import net.java.sip.communicator.impl.gui.main.contactlist.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.service.contactsource.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.account.*;
import net.java.sip.communicator.util.skin.*;
/**
* The <tt>ChooseCallAccountDialog</tt> is the dialog shown when calling a
* contact in order to let the user choose the account he'd prefer to use in
* order to call this contact.
*
* @author Yana Stamcheva
* @author Adam Netocny
*/
public class ChooseCallAccountPopupMenu
extends SIPCommPopupMenu
implements Skinnable
{
/**
* Serial version UID.
*/
private static final long serialVersionUID = 0L;
/**
* The invoker component.
*/
protected final JComponent invoker;
/**
* The call interface listener, which would be notified once the call
* interface is created.
*/
private CallInterfaceListener callInterfaceListener;
/**
* The <tt>MetaContact</tt> we're calling.
*/
private UIContactImpl uiContact;
/**
* Creates this dialog.
*
* @param invoker the invoker of this pop up menu
* @param contactToCall the contact to call
* @param telephonyProviders a list of all possible telephony providers
*/
public ChooseCallAccountPopupMenu(
JComponent invoker,
final String contactToCall,
List<ProtocolProviderService> telephonyProviders)
{
this(invoker, contactToCall, telephonyProviders,
OperationSetBasicTelephony.class);
}
/**
* Creates this dialog.
*
* @param invoker the invoker of this pop up menu
* @param contactToCall the contact to call
* @param telephonyProviders a list of all possible telephony providers
* @param l <tt>CallInterfaceListener</tt> instance
*/
public ChooseCallAccountPopupMenu(
JComponent invoker,
final String contactToCall,
List<ProtocolProviderService> telephonyProviders,
CallInterfaceListener l)
{
this(invoker, contactToCall, telephonyProviders,
OperationSetBasicTelephony.class);
callInterfaceListener = l;
}
/**
* Creates this dialog.
*
* @param invoker the invoker of this pop up menu
* @param contactToCall the contact to call
* @param telephonyProviders a list of all possible telephony providers
* @param opSetClass the operation set class indicating what operation
* would be performed when a given item is selected from the menu
*/
public ChooseCallAccountPopupMenu(
JComponent invoker,
final String contactToCall,
List<ProtocolProviderService> telephonyProviders,
Class<? extends OperationSet> opSetClass)
{
this.invoker = invoker;
this.init(GuiActivator.getResources()
.getI18NString(getI18NKeyCallVia()));
for (ProtocolProviderService provider : telephonyProviders)
{
this.addTelephonyProviderItem(provider, contactToCall, opSetClass);
}
}
/**
* Creates this dialog by specifying a list of telephony contacts to choose
* from.
*
* @param invoker the invoker of this pop up
* @param telephonyObjects the list of telephony contacts to select through
*/
public ChooseCallAccountPopupMenu( JComponent invoker,
List<?> telephonyObjects)
{
this( invoker,
telephonyObjects,
OperationSetBasicTelephony.class);
}
/**
* Creates this dialog by specifying a list of telephony contacts to choose
* from.
*
* @param invoker the invoker of this pop up
* @param telephonyObjects the list of telephony contacts to select through
* @param opSetClass the operation class, which indicates what action would
* be performed if an item is selected from the list
*/
public ChooseCallAccountPopupMenu(JComponent invoker,
List<?> telephonyObjects,
Class<? extends OperationSet> opSetClass)
{
this.invoker = invoker;
this.init(GuiActivator.getResources()
.getI18NString(getI18NKeyChooseContact()));
for (Object o : telephonyObjects)
{
if (o instanceof UIContactDetailImpl)
this.addTelephonyContactItem(
(UIContactDetailImpl) o, opSetClass);
else if (o instanceof ChatTransport)
this.addTelephonyChatTransportItem((ChatTransport) o,
opSetClass);
}
}
/**
* Returns the key to use for choose contact string. Can be overridden
* by extenders.
* @return the key to use for choose contact string.
*/
protected String getI18NKeyChooseContact()
{
return "service.gui.CHOOSE_CONTACT";
}
/**
* Returns the key to use for choose contact string. Can be overridden
* by extenders.
* @return the key to use for choose contact string.
*/
protected String getI18NKeyCallVia()
{
return "service.gui.CALL_VIA";
}
/**
* Initializes and add some common components.
*
* @param infoString the string we'd like to show on the top of this
* popup menu
*/
private void init(String infoString)
{
setInvoker(invoker);
this.add(createInfoLabel(infoString));
this.addSeparator();
this.setFocusable(true);
}
/**
* Adds the given <tt>telephonyProvider</tt> to the list of available
* telephony providers.
*
* @param telephonyProvider the provider to add.
* @param contactString the contact to call when the provider is selected
* @param opSetClass the operation set class indicating what action would
* be performed when an item is selected
*/
private void addTelephonyProviderItem(
final ProtocolProviderService telephonyProvider,
final String contactString,
final Class<? extends OperationSet> opSetClass)
{
final ProviderMenuItem providerItem
= new ProviderMenuItem(telephonyProvider);
providerItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if (uiContact != null)
itemSelected(
opSetClass,
providerItem.getProtocolProvider(),
contactString,
uiContact);
else
itemSelected(
opSetClass,
providerItem.getProtocolProvider(),
contactString);
if (callInterfaceListener != null)
callInterfaceListener.callInterfaceStarted();
ChooseCallAccountPopupMenu.this.setVisible(false);
}
});
this.add(providerItem);
}
/**
* Adds the given <tt>telephonyContact</tt> to the list of available
* telephony contact.
*
* @param telephonyContact the telephony contact to add
* @param opSetClass the operation set class, that indicates the action that
* would be performed when an item is selected
*/
private void addTelephonyContactItem(
final UIContactDetailImpl telephonyContact,
final Class<? extends OperationSet> opSetClass)
{
final ContactMenuItem contactItem
= new ContactMenuItem(telephonyContact);
contactItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
List<ProtocolProviderService> providers
= AccountUtils.getOpSetRegisteredProviders(
opSetClass,
telephonyContact.getPreferredProtocolProvider(opSetClass),
telephonyContact.getPreferredProtocol(opSetClass));
if (providers == null || providers.size() <= 0)
{
new ErrorDialog(null,
GuiActivator.getResources().getI18NString(
"service.gui.CALL_FAILED"),
GuiActivator.getResources().getI18NString(
"service.gui.NO_ONLINE_TELEPHONY_ACCOUNT"))
.showDialog();
return;
}
else if (providers.size() > 1)
{
itemSelected(
opSetClass, providers, telephonyContact.getAddress());
}
else // providersCount == 1
{
ProtocolProviderService provider = providers.get(0);
String contactAddress = telephonyContact.getAddress();
if (uiContact != null)
itemSelected(
opSetClass,
provider,
contactAddress,
uiContact);
else
itemSelected(
opSetClass,
provider,
contactAddress);
}
ChooseCallAccountPopupMenu.this.setVisible(false);
}
});
String category = telephonyContact.getCategory();
if (category != null && category.equals(ContactDetail.Category.Phone))
{
int index = findPhoneItemIndex();
if (index < 0)
add(contactItem);
else
insert(contactItem, findPhoneItemIndex());
}
else
{
Component lastComp = getComponent(getComponentCount() - 1);
if (lastComp instanceof ContactMenuItem)
category = ((ContactMenuItem) lastComp).getCategory();
if (category != null
&& category.equals(ContactDetail.Category.Phone))
addSeparator();
add(contactItem);
}
}
/**
* Returns the index of a phone menu item.
*
* @return the index of a phone menu item
*/
private int findPhoneItemIndex()
{
int index = -1;
for (int i = getComponentCount() - 1; i > 1; i--)
{
Component c = getComponent(i);
if (c instanceof ContactMenuItem)
{
String category = ((ContactMenuItem) c).getCategory();
if (category == null
|| !category.equals(ContactDetail.Category.Phone))
continue;
}
else if (c instanceof JSeparator)
index = i - 1;
else
return index;
}
return index;
}
/**
* Adds the given <tt>ChatTransport</tt> to the list of available
* telephony chat transports.
*
* @param telTransport the telephony chat transport to add
* @param opSetClass the class of the operation set indicating the operation
* to be executed in the item is selected
*/
private void addTelephonyChatTransportItem(
final ChatTransport telTransport,
final Class<? extends OperationSet> opSetClass)
{
final ChatTransportMenuItem transportItem
= new ChatTransportMenuItem(telTransport);
transportItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
ProtocolProviderService provider
= telTransport.getProtocolProvider();
String contactAddress = telTransport.getName();
if (uiContact != null)
CallManager.createCall(
opSetClass, provider, contactAddress, uiContact);
else
CallManager.createCall(
opSetClass, provider, contactAddress);
ChooseCallAccountPopupMenu.this.setVisible(false);
}
});
this.add(transportItem);
}
/**
* Shows the dialog at the given location.
*
* @param x the x coordinate
* @param y the y coordinate
*/
public void showPopupMenu(int x, int y)
{
setLocation(x, y);
setVisible(true);
}
/**
* Shows this popup menu regarding to its invoker location.
*/
public void showPopupMenu()
{
Point location = new Point(invoker.getX(),
invoker.getY() + invoker.getHeight());
SwingUtilities
.convertPointToScreen(location, invoker.getParent());
setLocation(location);
setVisible(true);
}
/**
* Sets the <tt>UIContactImpl</tt> we're currently calling.
*
* @param uiContact the <tt>UIContactImpl</tt> we're currently calling
*/
public void setUIContact(UIContactImpl uiContact)
{
this.uiContact = uiContact;
}
/**
* Creates the info label.
*
* @param infoString the string we'd like to show on the top of this
* popup menu
* @return the created info label
*/
private Component createInfoLabel(String infoString)
{
JMenuItem infoLabel = new JMenuItem();
infoLabel.setEnabled(false);
infoLabel.setFocusable(false);
infoLabel.setText("<html><b>" + infoString + "</b></html>");
return infoLabel;
}
/**
* Item was selected, give a chance for extenders to override.
*
* @param opSetClass the operation set to use.
* @param protocolProviderService the protocol provider
* @param contact the contact address
* @param uiContact the <tt>MetaContact</tt> selected
*/
protected void itemSelected(
Class<? extends OperationSet> opSetClass,
ProtocolProviderService protocolProviderService,
String contact,
UIContactImpl uiContact)
{
CallManager.createCall(
opSetClass,
protocolProviderService,
contact,
uiContact);
}
/**
* Item was selected, give a chance for extenders to override.
*
* @param opSetClass the operation set to use.
* @param protocolProviderService the protocol provider
* @param contact the contact address selected
*/
protected void itemSelected(Class<? extends OperationSet> opSetClass,
ProtocolProviderService protocolProviderService,
String contact)
{
CallManager.createCall(
opSetClass,
protocolProviderService,
contact);
}
/**
* Item was selected, give a chance for extenders to override.
*
* @param opSetClass the operation set to use.
* @param providers list of available protocol providers
* @param contact the contact address selected
*/
protected void itemSelected(Class<? extends OperationSet> opSetClass,
List<ProtocolProviderService> providers,
String contact)
{
ChooseCallAccountDialog callAccountDialog
= new ChooseCallAccountDialog(contact, opSetClass, providers);
if (uiContact != null)
callAccountDialog.setUIContact(uiContact);
callAccountDialog.setVisible(true);
}
/**
* A custom menu item corresponding to a specific
* <tt>ProtocolProviderService</tt>.
*/
private class ProviderMenuItem
extends JMenuItem
implements Skinnable
{
/**
* Serial version UID.
*/
private static final long serialVersionUID = 0L;
private final ProtocolProviderService protocolProvider;
public ProviderMenuItem(ProtocolProviderService protocolProvider)
{
this.protocolProvider = protocolProvider;
this.setText(protocolProvider.getAccountID().getDisplayName());
loadSkin();
}
public ProtocolProviderService getProtocolProvider()
{
return protocolProvider;
}
/**
* Reloads protocol icon.
*/
public void loadSkin()
{
byte[] protocolIcon
= protocolProvider.getProtocolIcon()
.getIcon(ProtocolIcon.ICON_SIZE_16x16);
if (protocolIcon != null)
this.setIcon(ImageLoader.getIndexedProtocolIcon(
ImageUtils.getBytesInImage(protocolIcon),
protocolProvider));
}
}
/**
* A custom menu item corresponding to a specific protocol <tt>Contact</tt>.
*/
private class ContactMenuItem
extends JMenuItem
implements Skinnable
{
/**
* Serial version UID.
*/
private static final long serialVersionUID = 0L;
private final UIContactDetailImpl contact;
public ContactMenuItem(UIContactDetailImpl contact)
{
this.contact = contact;
String itemName = "<html>";
Iterator<String> labels = contact.getLabels();
if (labels != null && labels.hasNext())
while (labels.hasNext())
itemName += "<b style=\"color: gray\">"
+ labels.next().toLowerCase() + "</b> ";
itemName += contact.getAddress() + "</html>";
this.setText(itemName);
loadSkin();
}
/**
* Returns the category of the underlying contact detail.
*
* @return the category of the underlying contact detail
*/
public String getCategory()
{
return contact.getCategory();
}
/**
* Reloads contact icon.
*/
public void loadSkin()
{
ImageIcon contactIcon = contact.getStatusIcon();
if (contactIcon == null)
{
PresenceStatus status = contact.getPresenceStatus();
BufferedImage statusIcon = null;
if (status != null)
statusIcon = Constants.getStatusIcon(status);
if (statusIcon != null)
contactIcon = ImageLoader.getIndexedProtocolIcon(
statusIcon,
contact.getPreferredProtocolProvider(null));
}
if (contactIcon != null)
this.setIcon(ImageLoader.getIndexedProtocolIcon(
contactIcon.getImage(),
contact.getPreferredProtocolProvider(null)));
}
}
/**
* A custom menu item corresponding to a specific <tt>ChatTransport</tt>.
*/
private class ChatTransportMenuItem
extends JMenuItem
implements Skinnable
{
/**
* Serial version UID.
*/
private static final long serialVersionUID = 0L;
private final ChatTransport chatTransport;
public ChatTransportMenuItem(ChatTransport chatTransport)
{
this.chatTransport = chatTransport;
this.setText(chatTransport.getName());
loadSkin();
}
/**
* Reloads transport icon.
*/
public void loadSkin()
{
PresenceStatus status = chatTransport.getStatus();
byte[] statusIconBytes = status.getStatusIcon();
Icon statusIcon = null;
if (statusIconBytes != null && statusIconBytes.length > 0)
{
statusIcon = ImageLoader.getIndexedProtocolIcon(
ImageUtils.getBytesInImage(statusIconBytes),
chatTransport.getProtocolProvider());
}
if (statusIcon != null)
this.setIcon(statusIcon);
}
}
/**
* Reloads all menu items.
*/
public void loadSkin()
{
Component[] components = getComponents();
for(Component component : components)
{
if(component instanceof Skinnable)
{
Skinnable skinnableComponent = (Skinnable) component;
skinnableComponent.loadSkin();
}
}
}
}