aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--resources/languages/resources.properties11
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/menus/AutoAnswerMenu.java616
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/menus/ToolsMenu.java5
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/OperationSetAutoAnswerSipImpl.java402
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java22
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java5
-rw-r--r--src/net/java/sip/communicator/service/protocol/OperationSetAutoAnswer.java101
-rw-r--r--src/net/java/sip/communicator/util/swing/SIPCommDialog.java9
8 files changed, 1160 insertions, 11 deletions
diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties
index 1f6baef..06b369a 100644
--- a/resources/languages/resources.properties
+++ b/resources/languages/resources.properties
@@ -318,6 +318,7 @@ service.gui.NEW_MESSAGE=New message
service.gui.NEW_NAME=New name
service.gui.NEW_STATUS_MESSAGE=New status message
service.gui.NO=No
+service.gui.NONE=None
service.gui.NO_CAMERA_AVAILABLE=No camera available
service.gui.NO_AVAILABLE_ROOMS=The list of rooms for this server is currently not available.
service.gui.NO_CONTACTS_FOUND=No matching contacts found. Press Ctrl+Enter to call {0} or use the buttons below.
@@ -576,6 +577,16 @@ service.gui.CONTINUE=Continue
service.gui.SHOW_CERT=Show Certificate
service.gui.HIDE_CERT=Hide Certificate
+service.gui.AUTO_ANSWER=Auto answer
+service.gui.AUTO_ANSWER_ALL_CALLS=All calls
+service.gui.AUTO_ANSWER_ALERT_INFO_FIELDS=Only calls that require it. (Those containing an \"Alert-Info\" field set to \"Auto Answer\")
+service.gui.AUTO_ANSWER_CUSTOM_FIELDS=Calls with the following field and value
+service.gui.AUTO_ANSWER_FIELD=Field/Header:
+service.gui.AUTO_ANSWER_VALUE=Value:
+service.gui.AUTO_ANSWER_DESCR_VLUE=leave empty for any
+service.gui.AUTO_ANSWER_FWD_CALLS=Forward Calls
+service.gui.AUTO_ANSWER_FWD_CALLS_TO=Forward all calls to the following number or URI:
+
service.gui.avatar.CHOOSE_ICON=Choose picture
service.gui.avatar.CLEAR_RECENT=Clear recent pictures
service.gui.avatar.RECENT_ICONS=Recent pictures:
diff --git a/src/net/java/sip/communicator/impl/gui/main/menus/AutoAnswerMenu.java b/src/net/java/sip/communicator/impl/gui/main/menus/AutoAnswerMenu.java
new file mode 100644
index 0000000..0aea3f4
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/menus/AutoAnswerMenu.java
@@ -0,0 +1,616 @@
+/*
+ * Jitsi, 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.menus;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.utils.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.resources.*;
+import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.skin.*;
+import net.java.sip.communicator.util.swing.*;
+import org.osgi.framework.*;
+
+import javax.swing.*;
+import javax.swing.border.*;
+
+/**
+ * The auto answer menu dynamically adds/removes menu items for enabled/disabled
+ * protocol providers.
+ *
+ * @author Damian Minkov
+ */
+public class AutoAnswerMenu
+ extends SIPCommMenu
+ implements Skinnable,
+ ServiceListener
+{
+ /**
+ * Creates the menu and load already registered providers.
+ */
+ public AutoAnswerMenu()
+ {
+ super(GuiActivator.getResources()
+ .getI18NString("service.gui.AUTO_ANSWER"));
+
+ loadSkin();
+
+ GuiActivator.bundleContext.addServiceListener(this);
+
+ this.registerMenuItems();
+ }
+
+ /**
+ * Registers all menu items.
+ */
+ private void registerMenuItems()
+ {
+ for (ProtocolProviderFactory providerFactory : GuiActivator
+ .getProtocolProviderFactories().values())
+ {
+ ServiceReference serRef;
+ ProtocolProviderService protocolProvider;
+
+ for (AccountID accountID : providerFactory.getRegisteredAccounts())
+ {
+ serRef = providerFactory.getProviderForAccount(accountID);
+
+ protocolProvider =
+ (ProtocolProviderService) GuiActivator.bundleContext
+ .getService(serRef);
+
+ addAccount(protocolProvider);
+ }
+ }
+ }
+
+ /**
+ * Adds a menu item for the account given by <tt>protocolProvider</tt>.
+ * @param protocolProvider the <tt>ProtocolProviderService</tt>, for which
+ * to add a menu
+ */
+ public void addAccount(ProtocolProviderService protocolProvider)
+ {
+ OperationSetAutoAnswer opset = protocolProvider
+ .getOperationSet(OperationSetAutoAnswer.class);
+
+ if(opset == null)
+ {
+ return;
+ }
+
+ boolean isHidden
+ = protocolProvider
+ .getAccountID()
+ .getAccountProperty(
+ ProtocolProviderFactory.IS_PROTOCOL_HIDDEN)
+ != null;
+
+ if (isHidden)
+ return;
+
+ AutoAnswerMenuItem providerMenu =
+ new AutoAnswerMenuItem(protocolProvider);
+
+ boolean isMenuAdded = false;
+ AccountID accountId = protocolProvider.getAccountID();
+ // If we already have other accounts.
+ for(int i = 0; i < getItemCount(); i++)
+ {
+ JMenuItem c = getItem(i);
+ if (!(c instanceof AutoAnswerMenuItem))
+ continue;
+
+ AutoAnswerMenuItem menu = (AutoAnswerMenuItem) c;
+ int menuIndex = getPopupMenu().getComponentIndex(menu);
+
+ AccountID menuAccountID = menu.getProtocolProvider().getAccountID();
+
+ int protocolCompare = accountId.getProtocolDisplayName().compareTo(
+ menuAccountID.getProtocolDisplayName());
+
+ // If the new account protocol name is before the name of the menu
+ // we insert the new account before the given menu.
+ if (protocolCompare < 0)
+ {
+ insert(providerMenu, menuIndex);
+ isMenuAdded = true;
+ break;
+ }
+ else if (protocolCompare == 0)
+ {
+ // If we have the same protocol name, we check the account name.
+ if (accountId.getDisplayName()
+ .compareTo(menuAccountID.getDisplayName()) < 0)
+ {
+ insert( providerMenu, menuIndex);
+ isMenuAdded = true;
+ break;
+ }
+ }
+ }
+
+ if (!isMenuAdded)
+ add(providerMenu);
+ }
+
+ /**
+ * Remove menu item for the account given by <tt>protocolProvider</tt>.
+ * @param protocolProvider the <tt>ProtocolProviderService</tt>, for which
+ * to remove the menu
+ */
+ public void removeAccount(ProtocolProviderService protocolProvider)
+ {
+ for(int i = 0; i < getItemCount(); i++)
+ {
+ JMenuItem c = getItem(i);
+
+ if (!(c instanceof AutoAnswerMenuItem))
+ continue;
+
+ AutoAnswerMenuItem menu = (AutoAnswerMenuItem) c;
+
+ AccountID menuAccountID = menu.getProtocolProvider().getAccountID();
+
+ if(menuAccountID.equals(protocolProvider.getAccountID()))
+ {
+ this.remove(menu);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Loads menu item icons.
+ */
+ public void loadSkin()
+ {
+ this.setIcon(new ImageIcon(
+ ImageLoader.getImage(ImageLoader.CALL_16x16_ICON)));
+ }
+
+ /**
+ * Implements the <tt>ServiceListener</tt> method. Verifies whether the
+ * passed event concerns a <tt>ProtocolProviderService</tt> and adds the
+ * corresponding UI controls in the menu.
+ *
+ * @param event The <tt>ServiceEvent</tt> object.
+ */
+ public void serviceChanged(ServiceEvent event)
+ {
+ ServiceReference serviceRef = event.getServiceReference();
+
+ // if the event is caused by a bundle being stopped, we don't want to
+ // know
+ if (serviceRef.getBundle().getState() == Bundle.STOPPING)
+ {
+ return;
+ }
+
+ Object service = GuiActivator.bundleContext.getService(serviceRef);
+
+ // we don't care if the source service is not a protocol provider
+ if (!(service instanceof ProtocolProviderService))
+ {
+ return;
+ }
+
+ switch (event.getType())
+ {
+ case ServiceEvent.REGISTERED:
+ this.addAccount((ProtocolProviderService) service);
+ break;
+ case ServiceEvent.UNREGISTERING:
+ this.removeAccount((ProtocolProviderService) service);
+ break;
+ }
+ }
+
+ /**
+ * Represent menu item for provider.
+ */
+ private class AutoAnswerMenuItem
+ extends JMenuItem
+ implements ActionListener
+ {
+ /**
+ * The provider.
+ */
+ private ProtocolProviderService providerService;
+
+ /**
+ * Init the menu item.
+ * @param provider the provider.
+ */
+ AutoAnswerMenuItem(ProtocolProviderService provider)
+ {
+ this(provider,
+ provider.getAccountID().getDisplayName(),
+ ImageUtils.getBytesInImage(
+ provider.getProtocolIcon().getIcon(
+ ProtocolIcon.ICON_SIZE_16x16)));
+ }
+
+ /**
+ * Creates the menu item.
+ * @param provider the provider.
+ * @param displayName the display name of the item.
+ * @param onlineImage the icon to display
+ */
+ private AutoAnswerMenuItem(ProtocolProviderService provider,
+ String displayName,
+ Image onlineImage)
+ {
+ super(displayName, new ImageIcon(onlineImage));
+ this.providerService = provider;
+
+ this.addActionListener(this);
+ }
+
+ /**
+ * Returns the protocol provider associated with this menu.
+ * @return the protocol provider associated with this menu
+ */
+ public ProtocolProviderService getProtocolProvider()
+ {
+ return providerService;
+ }
+
+ /**
+ * When action is performed on the item show a dialog.
+ * @param e
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ new AutoAnswerOptionsDialog(providerService).setVisible(true);
+ }
+ }
+
+ /**
+ * The dialog to config auto answer functionality for a provider.
+ */
+ private class AutoAnswerOptionsDialog
+ extends SIPCommDialog
+ implements ActionListener
+ {
+ /**
+ * Header name.
+ */
+ private static final String AUTO_ALERT_INFO_NAME = "Alert-Info";
+
+ /**
+ * Header name.
+ */
+ private static final String AUTO_ALERT_INFO_VALUE = "Auto Answer";
+
+ /**
+ * The provider.
+ */
+ private ProtocolProviderService providerService;
+
+ /**
+ * The ok button.
+ */
+ private final JButton okButton = new JButton(
+ GuiActivator.getResources().getI18NString("service.gui.OK"));
+
+ /**
+ * The cancel button.
+ */
+ private final JButton cancelButton = new JButton(
+ GuiActivator.getResources().getI18NString("service.gui.CANCEL"));
+
+ /**
+ * None radio button.
+ */
+ private JRadioButton noneRadio;
+
+ /**
+ * Unconditional radio button.
+ */
+ private JRadioButton alwaysAnswerRadio;
+
+ /**
+ * Alert info radio button.
+ */
+ private JRadioButton alertInfoValue;
+
+ /**
+ * Custom field radio button.
+ */
+ private JRadioButton customValueRadio;
+
+ /**
+ * Custom field name text field.
+ */
+ private JTextField headerNameField = new JTextField();
+
+ /**
+ * Custom value name text field.
+ */
+ private JTextField headerValueField = new JTextField();
+
+ /**
+ * Call fwd radio button.
+ */
+ private JRadioButton callFwd;
+
+ /**
+ * Call fwd number field.
+ */
+ private JTextField callFwdNumberField = new JTextField();
+
+ /**
+ * Create dialog.
+ * @param providerService provider.
+ */
+ AutoAnswerOptionsDialog(ProtocolProviderService providerService)
+ {
+ super(false);
+
+ this.providerService = providerService;
+
+ this.setTitle(GuiActivator.getResources()
+ .getI18NString("service.gui.AUTO_ANSWER"));
+
+ initComponents();
+
+ loadValues();
+ }
+
+ /**
+ * Creates panel.
+ */
+ private void initComponents()
+ {
+ ResourceManagementService R = GuiActivator.getResources();
+ ButtonGroup group = new ButtonGroup();
+ JPanel mainPanel = new TransparentPanel(new GridBagLayout());
+
+ int currentRow = 0;
+ GridBagConstraints c = new GridBagConstraints();
+ c.gridx = c.gridy = currentRow++;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.fill = GridBagConstraints.HORIZONTAL;
+
+ c.gridy = currentRow++;
+ noneRadio = new SIPCommRadioButton(
+ R.getI18NString("service.gui.NONE"));
+ noneRadio.setSelected(true);
+ group.add(noneRadio);
+ mainPanel.add(noneRadio, c);
+
+ c.gridy = currentRow++;
+ mainPanel.add(
+ getTitlePanel(R.getI18NString("service.gui.AUTO_ANSWER")), c);
+
+ c.gridy = currentRow++;
+ alwaysAnswerRadio = new SIPCommRadioButton(
+ R.getI18NString("service.gui.AUTO_ANSWER_ALL_CALLS"));
+ group.add(alwaysAnswerRadio);
+ mainPanel.add(alwaysAnswerRadio, c);
+
+ c.gridy = currentRow++;
+ alertInfoValue = new SIPCommRadioButton(
+ R.getI18NString("service.gui.AUTO_ANSWER_ALERT_INFO_FIELDS"));
+ group.add(alertInfoValue);
+ mainPanel.add(alertInfoValue, c);
+
+ c.gridy = currentRow++;
+ customValueRadio = new SIPCommRadioButton(
+ R.getI18NString("service.gui.AUTO_ANSWER_CUSTOM_FIELDS"));
+ group.add(customValueRadio);
+ mainPanel.add(customValueRadio, c);
+
+ JPanel customHeaderPanel = new TransparentPanel(
+ new GridLayout(1, 2));
+ JPanel namePanel = new TransparentPanel(new BorderLayout());
+ namePanel.add(
+ new JLabel(R.getI18NString("service.gui.AUTO_ANSWER_FIELD")),
+ BorderLayout.WEST);
+ namePanel.add(headerNameField, BorderLayout.CENTER);
+ JPanel valuePanel = new TransparentPanel(new BorderLayout());
+ valuePanel.add(
+ new JLabel(R.getI18NString("service.gui.AUTO_ANSWER_VALUE")),
+ BorderLayout.WEST);
+ valuePanel.add(headerValueField, BorderLayout.CENTER);
+ customHeaderPanel.add(namePanel);
+ customHeaderPanel.add(valuePanel);
+
+ c.gridy = currentRow++;
+ c.insets = new Insets(0, 28, 0, 0);
+ mainPanel.add(customHeaderPanel, c);
+
+ String description =
+ R.getI18NString("service.gui.AUTO_ANSWER_DESCR_VLUE");
+ JLabel descriptionLabel = new JLabel(description);
+ descriptionLabel.setToolTipText(description);
+ descriptionLabel.setForeground(Color.GRAY);
+ descriptionLabel.setFont(descriptionLabel.getFont().deriveFont(8));
+ descriptionLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 8, 0));
+ descriptionLabel.setHorizontalAlignment(JLabel.RIGHT);
+
+ c.gridy = currentRow++;
+ mainPanel.add(descriptionLabel, c);
+
+ c.gridy = currentRow++;
+ c.insets = new Insets(0, 0, 0, 0);
+ mainPanel.add(getTitlePanel(
+ R.getI18NString("service.gui.AUTO_ANSWER_FWD_CALLS")), c);
+
+ c.gridy = currentRow++;
+ callFwd = new SIPCommRadioButton(
+ R.getI18NString("service.gui.AUTO_ANSWER_FWD_CALLS_TO"));
+ group.add(callFwd);
+ mainPanel.add(callFwd, c);
+
+ c.gridy = currentRow++;
+ c.insets = new Insets(0, 28, 0, 0);
+ mainPanel.add(callFwdNumberField, c);
+
+ TransparentPanel buttonsPanel
+ = new TransparentPanel(new FlowLayout(FlowLayout.RIGHT));
+
+ this.getRootPane().setDefaultButton(okButton);
+ okButton.setMnemonic(
+ GuiActivator.getResources().getI18nMnemonic("service.gui.OK"));
+ cancelButton.setMnemonic(
+ GuiActivator.getResources().getI18nMnemonic("service.gui.CANCEL"));
+
+ okButton.addActionListener(this);
+ cancelButton.addActionListener(this);
+
+ buttonsPanel.add(okButton);
+ buttonsPanel.add(cancelButton);
+
+ c.gridy = currentRow++;
+ c.insets = new Insets(0, 0, 0, 0);
+ mainPanel.add(buttonsPanel, c);
+
+ mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+ this.setLayout(new BorderLayout());
+ this.add(mainPanel, BorderLayout.CENTER);
+ }
+
+ /**
+ * Creates separator with text.
+ * @param title the title
+ * @return the panel separator.
+ */
+ private JPanel getTitlePanel(String title)
+ {
+ JLabel label = new JLabel(title);
+ label.setBorder(new EmptyBorder(0, 0, 0, 10));
+ label.setFont(UIManager.getFont("TitledBorder.font"));
+ label.setForeground(UIManager
+ .getColor("TitledBorder.titleColor"));
+
+ JPanel pnlSectionName = new TransparentPanel();
+ pnlSectionName.setLayout(new GridBagLayout());
+ GridBagConstraints c = new GridBagConstraints();
+ c.gridx = c.gridy = 0;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.gridwidth = 2;
+ pnlSectionName.add(label, c);
+ c.gridx = 2;
+ c.weightx = 1;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ pnlSectionName.add(new JSeparator(), c);
+
+ JPanel pnlSection = new TransparentPanel()
+ {
+ @Override
+ public Component add(Component comp)
+ {
+ if(comp instanceof JComponent)
+ ((JComponent)comp).setAlignmentX(LEFT_ALIGNMENT);
+ return super.add(comp);
+ }
+ };
+ pnlSection.setLayout(new BoxLayout(pnlSection, BoxLayout.Y_AXIS));
+ pnlSection.add(pnlSectionName);
+
+ return pnlSection;
+ }
+
+ /**
+ * Saves settings.
+ * @param e the event on button.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if(e.getSource().equals(okButton))
+ {
+ OperationSetAutoAnswer opset = providerService
+ .getOperationSet(OperationSetAutoAnswer.class);
+
+ if(noneRadio.isSelected())
+ {
+ opset.clear();
+ }
+ else if(alwaysAnswerRadio.isSelected())
+ {
+ opset.setAutoAnswerUnconditional();
+ }
+ else if(alertInfoValue.isSelected())
+ {
+ opset.setAutoAnswerCondition(
+ AUTO_ALERT_INFO_NAME,
+ AUTO_ALERT_INFO_VALUE);
+ }
+ else if(customValueRadio.isSelected())
+ {
+ opset.setAutoAnswerCondition(
+ headerNameField.getText(),
+ headerValueField.getText());
+ }
+ else if(callFwd.isSelected())
+ {
+ opset.setCallForward(callFwdNumberField.getText());
+ }
+ }
+
+ dispose();
+ }
+
+ /**
+ * Esc pressed.
+ * @param isEscaped indicates if this frame has been closed by
+ * pressing the escape
+ */
+ protected void close(boolean isEscaped)
+ {
+ dispose();
+ }
+
+
+ /**
+ * Populate values from opset to local components.
+ */
+ private void loadValues()
+ {
+ OperationSetAutoAnswer opset = providerService
+ .getOperationSet(OperationSetAutoAnswer.class);
+
+ if(opset == null)
+ return;
+
+ noneRadio.setSelected(true);
+ alwaysAnswerRadio.setSelected(
+ opset.isAutoAnswerUnconditionalSet());
+
+ if(opset.isAutoAnswerConditionSet())
+ {
+ String fName = opset.getAutoAnswerHeaderName();
+ String fValue = opset.getAutoAnswerHeaderValue();
+ if(AUTO_ALERT_INFO_NAME.equals(fName)
+ && AUTO_ALERT_INFO_VALUE.equals(fValue))
+ {
+ alertInfoValue.setSelected(true);
+ }
+ else
+ {
+ customValueRadio.setSelected(true);
+ headerNameField.setText(fName);
+
+ if(!StringUtils.isNullOrEmpty(fValue))
+ headerValueField.setText(fValue);
+ }
+
+ }
+
+ if(!StringUtils.isNullOrEmpty(opset.getCallForward()))
+ {
+ callFwd.setSelected(true);
+ callFwdNumberField.setText(opset.getCallForward());
+ }
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/menus/ToolsMenu.java b/src/net/java/sip/communicator/impl/gui/main/menus/ToolsMenu.java
index 6e4c060..ec0bf51 100644
--- a/src/net/java/sip/communicator/impl/gui/main/menus/ToolsMenu.java
+++ b/src/net/java/sip/communicator/impl/gui/main/menus/ToolsMenu.java
@@ -242,6 +242,11 @@ public class ToolsMenu
conferenceMenuItem.addActionListener(this);
this.add(conferenceMenuItem);
+ AutoAnswerMenu autoAnswerMenu = new AutoAnswerMenu();
+ this.add(autoAnswerMenu);
+
+ this.addSeparator();
+
// Show/hide offline contacts menu item.
String offlineTextKey = ConfigurationManager.isShowOffline()
? "service.gui.HIDE_OFFLINE_CONTACTS"
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetAutoAnswerSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetAutoAnswerSipImpl.java
new file mode 100644
index 0000000..5653fed
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetAutoAnswerSipImpl.java
@@ -0,0 +1,402 @@
+/*
+ * Jitsi, 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.protocol.sip;
+
+import gov.nist.javax.sip.header.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.*;
+
+import javax.sip.*;
+import javax.sip.address.*;
+import javax.sip.header.*;
+import javax.sip.message.*;
+import java.util.*;
+
+/**
+ * An Operation Set defining options to auto answer/forward incoming calls.
+ * Forward calls to specified number using same provider.
+ * Auto answering calls unconditional, on existence of certain header name, or
+ * on existence of specified header name and value.
+ *
+ * @author Damian Minkov
+ */
+public class OperationSetAutoAnswerSipImpl
+ extends CallPeerAdapter
+ implements OperationSetAutoAnswer
+{
+ /**
+ * Our class logger.
+ */
+ private static final Logger logger =
+ Logger.getLogger(OperationSetBasicTelephonySipImpl.class);
+
+ /**
+ * The parent operation set.
+ */
+ private OperationSetBasicTelephonySipImpl telephonySip;
+
+ /**
+ * Should we unconditionally answer.
+ */
+ private boolean answerUnconditional = false;
+
+ /**
+ * Should we answer on existence of some header and/or name.
+ */
+ private boolean answerConditional = false;
+
+ /**
+ * The header name to look for.
+ */
+ private String headerName = null;
+
+ /**
+ * The header value to look for, if specified.
+ */
+ private String headerValue = null;
+
+ /**
+ * The call number to use for forwarding calls.
+ */
+ private String callFwdTo = null;
+
+ /**
+ * Creates this operation set, loads stored values, populating
+ * local variable settings.
+ *
+ * @param telephonySip the parent opset.
+ */
+ OperationSetAutoAnswerSipImpl(
+ OperationSetBasicTelephonySipImpl telephonySip)
+ {
+ this.telephonySip = telephonySip;
+
+ // init values from account props
+ load();
+ }
+
+ /**
+ * Load values from account properties.
+ */
+ private void load()
+ {
+ AccountID acc = telephonySip.getProtocolProvider().getAccountID();
+
+ answerUnconditional =
+ acc.getAccountPropertyBoolean(AUTO_ANSWER_UNCOND_PROP, false);
+
+ headerName =
+ acc.getAccountPropertyString(AUTO_ANSWER_COND_NAME_PROP);
+ headerValue =
+ acc.getAccountPropertyString(AUTO_ANSWER_COND_VALUE_PROP);
+ if(!StringUtils.isNullOrEmpty(headerName))
+ answerConditional = true;
+
+ callFwdTo =
+ acc.getAccountPropertyString(AUTO_ANSWER_FWD_NUM_PROP);
+ }
+
+ /**
+ * Saves values to account properties.
+ */
+ private void save()
+ {
+ AccountID acc = telephonySip.getProtocolProvider().getAccountID();
+ Map<String, String> accProps = acc.getAccountProperties();
+
+ // lets clear anything before saving :)
+ accProps.put(AUTO_ANSWER_UNCOND_PROP, null);
+ accProps.put(AUTO_ANSWER_COND_NAME_PROP, null);
+ accProps.put(AUTO_ANSWER_COND_VALUE_PROP, null);
+ accProps.put(AUTO_ANSWER_FWD_NUM_PROP, null);
+
+ if(answerUnconditional)
+ {
+ accProps.put(AUTO_ANSWER_UNCOND_PROP, Boolean.TRUE.toString());
+ }
+ else if(answerConditional)
+ {
+ accProps.put(AUTO_ANSWER_COND_NAME_PROP, headerName);
+
+ if(!StringUtils.isNullOrEmpty(headerValue))
+ accProps.put(AUTO_ANSWER_COND_VALUE_PROP, headerValue);
+ }
+ else if(!StringUtils.isNullOrEmpty(callFwdTo))
+ {
+ accProps.put(AUTO_ANSWER_FWD_NUM_PROP, callFwdTo);
+ }
+
+ acc.setAccountProperties(accProps);
+ SipActivator.getProtocolProviderFactory().storeAccount(acc);
+ }
+
+ /**
+ * Sets the auto answer option to unconditionally answer all incoming calls.
+ */
+ public void setAutoAnswerUnconditional()
+ {
+ clearLocal();
+
+ this.answerUnconditional = true;
+
+ save();
+ }
+
+ /**
+ * Is the auto answer option set to unconditionally
+ * answer all incoming calls.
+ * @return is auto answer set to unconditional.
+ */
+ public boolean isAutoAnswerUnconditionalSet()
+ {
+ return answerUnconditional;
+ }
+
+ /**
+ * Sets a specified header and its value if they exist in the incoming
+ * call packet this will activate auto answer.
+ * If value is empty or null it will be considered as any (will search
+ * only for a header with that name and ignore the value)
+ * @param headerName the name of the header to search
+ * @param value the value for the header, can be null.
+ */
+ public void setAutoAnswerCondition(String headerName, String value)
+ {
+ clearLocal();
+
+ this.answerConditional = true;
+ this.headerName = headerName;
+ this.headerValue = value;
+
+ save();
+ }
+
+ /**
+ * Is the auto answer option set to conditionally
+ * answer all incoming calls.
+ * @return is auto answer set to conditional.
+ */
+ public boolean isAutoAnswerConditionSet()
+ {
+ return answerConditional;
+ }
+
+ /**
+ * Set to automatically forward all calls to the specified
+ * number using the same provider.
+ * @param numberTo number to use for forwarding
+ */
+ public void setCallForward(String numberTo)
+ {
+ clearLocal();
+
+ this.callFwdTo = numberTo;
+
+ save();
+ }
+
+ /**
+ * Get the value for automatically forward all calls to the specified
+ * number using the same provider..
+ * @return numberTo number to use for forwarding
+ */
+ public String getCallForward()
+ {
+ return this.callFwdTo;
+ }
+
+ /**
+ * Clear local settings.
+ */
+ private void clearLocal()
+ {
+ this.answerUnconditional = false;
+ this.answerConditional = false;
+ this.headerName = null;
+ this.headerValue = null;
+ this.callFwdTo = null;
+ }
+
+ /**
+ * Clear any previous settings.
+ */
+ public void clear()
+ {
+ clearLocal();
+
+ save();
+ }
+
+ /**
+ * Returns the name of the header if conditional auto answer is set.
+ * @return the name of the header if conditional auto answer is set.
+ */
+ public String getAutoAnswerHeaderName()
+ {
+ return headerName;
+ }
+
+ /**
+ * Returns the value of the header for the conditional auto answer.
+ * @return the value of the header for the conditional auto answer.
+ */
+ public String getAutoAnswerHeaderValue()
+ {
+ return headerValue;
+ }
+
+ /**
+ * Makes a check before locally creating call, should we just forward it.
+ * @param invite the current invite to check.
+ * @param serverTransaction the transaction.
+ * @return <tt>true</tt> if we have processed and no further processing is
+ * needed, <tt>false</tt> otherwise.
+ */
+ boolean preCallCheck(Request invite,
+ ServerTransaction serverTransaction)
+ {
+ if(StringUtils.isNullOrEmpty(callFwdTo))
+ return false;
+
+ Response response;
+ try
+ {
+ if (logger.isTraceEnabled())
+ logger.trace("will send moved temporally response: ");
+
+ response = telephonySip.getProtocolProvider().getMessageFactory()
+ .createResponse(Response.MOVED_TEMPORARILY, invite);
+
+ ContactHeader contactHeader =
+ (ContactHeader)response.getHeader(ContactHeader.NAME);
+ AddressFactory addressFactory =
+ telephonySip.getProtocolProvider().getAddressFactory();
+
+ String destination = getCallForward();
+ if(!destination.startsWith("sip"))
+ destination = "sip:" + destination;
+
+ contactHeader.setAddress(addressFactory.createAddress(
+ addressFactory.createURI(destination)));
+
+ serverTransaction.sendResponse(response);
+ if (logger.isDebugEnabled())
+ logger.debug("sent a moved temporally response: "
+ + response);
+ }
+ catch (Throwable ex)
+ {
+ logger.error("Error while trying to send a request", ex);
+ }
+
+ return true;
+ }
+
+ /**
+ * Makes a check after creating call locally, should we answer it.
+ * @param invite the current invite to check.
+ * @param call the created call to answer if needed.
+ * @return <tt>true</tt> if we have processed and no further processing is
+ * needed, <tt>false</tt> otherwise.
+ */
+ boolean followCallCheck(Request invite,
+ CallSipImpl call)
+ {
+ if(!(answerConditional || answerUnconditional))
+ return false;
+
+ // lets check for headers
+ if(answerConditional)
+ {
+ SIPHeader callAnswerHeader =
+ (SIPHeader)invite.getHeader(headerName);
+
+ if(callAnswerHeader == null)
+ return false;
+
+ if(!StringUtils.isNullOrEmpty(headerValue))
+ {
+ String value = callAnswerHeader.getHeaderValue();
+
+ if(value == null || !headerValue.equals(value))
+ return false;
+ }
+ }
+
+ // we are here cause we satisfy the conditional,
+ // or unconditional is true
+ Iterator<? extends CallPeer> peers = call.getCallPeers();
+
+ while (peers.hasNext())
+ {
+ final CallPeer peer = peers.next();
+
+ answerPeer(peer);
+ }
+
+ return true;
+ }
+
+ /**
+ * Answers call if peer in correct state or wait for it.
+ * @param peer the peer to check and answer.
+ */
+ private void answerPeer(final CallPeer peer)
+ {
+ CallPeerState state = peer.getState();
+
+ if (state == CallPeerState.INCOMING_CALL)
+ {
+ // answer in separate thread, don't block current
+ // processing
+ new Thread(new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ telephonySip.answerCallPeer(peer);
+ }
+ catch (OperationFailedException e)
+ {
+ logger.error("Could not answer to : " + peer
+ + " caused by the following exception: " + e);
+ }
+ }
+ }, getClass().getName()).start();
+ }
+ else
+ {
+ peer.addCallPeerListener(this);
+ }
+ }
+
+ /**
+ * If we peer was not in proper state wait for it and then answer.
+ * @param evt the <tt>CallPeerChangeEvent</tt> instance containing the
+ */
+ public void peerStateChanged(CallPeerChangeEvent evt)
+ {
+
+ CallPeerState newState = (CallPeerState) evt.getNewValue();
+
+ if (newState == CallPeerState.INCOMING_CALL)
+ {
+ CallPeer peer = evt.getSourceCallPeer();
+
+ peer.removeCallPeerListener(this);
+
+ answerPeer(peer);
+ }
+ else if (newState == CallPeerState.DISCONNECTED
+ || newState == CallPeerState.FAILED)
+ {
+ evt.getSourceCallPeer().removeCallPeerListener(this);
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
index cf7430c..c9b4ccb 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
@@ -991,6 +991,10 @@ public class OperationSetBasicTelephonySipImpl
CallPeerSipImpl existingPeer
= activeCallsRepository.findCallPeer(dialog);
+ OperationSetAutoAnswerSipImpl autoAnswerOpSet =
+ (OperationSetAutoAnswerSipImpl)
+ protocolProvider.getOperationSet(OperationSetAutoAnswer.class);
+
if(existingPeer == null)
{
//this is not a reINVITE. check if it's a transfer
@@ -1000,14 +1004,26 @@ public class OperationSetBasicTelephonySipImpl
if (replacesHeader == null)
{
- //this is a brand new call (not a transfered one)
+ // checks for forward of call, if no further processing
+ // is needed return
+ if(autoAnswerOpSet != null
+ && autoAnswerOpSet.preCallCheck(invite, serverTransaction))
+ return;
+
+ //this is a brand new call (not a transferred one)
CallSipImpl call = new CallSipImpl(this);
call.processInvite(sourceProvider, serverTransaction);
+
+ // checks for auto answering of call, if no further processing
+ // is needed return
+ if(autoAnswerOpSet != null
+ && autoAnswerOpSet.followCallCheck(invite, call))
+ return;
}
else
{
- //this is a transfered call which is replacing an existing one
- //(i.e. an attended transfer).
+ //this is a transferred call which is replacing an
+ // existing one (i.e. an attended transfer).
existingPeer = activeCallsRepository.findCallPeer(
replacesHeader.getCallId(),
replacesHeader.getToTag(),
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java
index 161c0b1..c4e224d 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java
@@ -419,6 +419,11 @@ public class ProtocolProviderServiceSipImpl
OperationSetAdvancedTelephony.class,
opSetBasicTelephonySipImpl);
+ addSupportedOperationSet(
+ OperationSetAutoAnswer.class,
+ new OperationSetAutoAnswerSipImpl(
+ opSetBasicTelephonySipImpl));
+
// init call security
addSupportedOperationSet(
OperationSetSecureZrtpTelephony.class,
diff --git a/src/net/java/sip/communicator/service/protocol/OperationSetAutoAnswer.java b/src/net/java/sip/communicator/service/protocol/OperationSetAutoAnswer.java
new file mode 100644
index 0000000..fbde708
--- /dev/null
+++ b/src/net/java/sip/communicator/service/protocol/OperationSetAutoAnswer.java
@@ -0,0 +1,101 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.service.protocol;
+
+/**
+ * An Operation Set defining options to auto answer/forward incoming calls.
+ *
+ * @author Damian Minkov
+ */
+public interface OperationSetAutoAnswer
+ extends OperationSet
+{
+ /**
+ * Auto answer unconditional account property.
+ */
+ public static final String AUTO_ANSWER_UNCOND_PROP =
+ "AUTO_ANSWER_UNCONDITIONAL";
+
+ /**
+ * Auto answer conditional account property - field name.
+ */
+ public static final String AUTO_ANSWER_COND_NAME_PROP =
+ "AUTO_ANSWER_CONDITIONAL_NAME";
+
+ /**
+ * Auto answer conditional account property - field value.
+ */
+ public static final String AUTO_ANSWER_COND_VALUE_PROP =
+ "AUTO_ANSWER_CONDITIONAL_VALUE";
+
+ /**
+ * Auto forward all calls account property.
+ */
+ public static final String AUTO_ANSWER_FWD_NUM_PROP =
+ "AUTO_ANSWER_FWD_NUM";
+
+ /**
+ * Sets the auto answer option to unconditionally answer all incoming calls.
+ */
+ public void setAutoAnswerUnconditional();
+
+ /**
+ * Is the auto answer option set to unconditionally
+ * answer all incoming calls.
+ * @return is auto answer set to unconditional.
+ */
+ public boolean isAutoAnswerUnconditionalSet();
+
+ /**
+ * Sets a specified header and its value if they exist in the incoming
+ * call packet this will activate auto answer.
+ * If value is empty or null it will be considered as any (will search
+ * only for a header with that name and ignore the value)
+ * @param headerName the name of the header to search
+ * @param value the value for the header, can be null.
+ */
+ public void setAutoAnswerCondition(String headerName, String value);
+
+ /**
+ * Is the auto answer option set to conditionally
+ * answer all incoming calls.
+ * @return is auto answer set to conditional.
+ */
+ public boolean isAutoAnswerConditionSet();
+
+ /**
+ * Returns the name of the header if conditional auto answer is set.
+ * @return the name of the header if conditional auto answer is set.
+ */
+ public String getAutoAnswerHeaderName();
+
+ /**
+ * Returns the value of the header for the conditional auto answer.
+ * @return the value of the header for the conditional auto answer.
+ */
+ public String getAutoAnswerHeaderValue();
+
+
+ /**
+ * Set to automatically forward all calls to the specified
+ * number using the same provider.
+ * @param numberTo number to use for forwarding
+ */
+ public void setCallForward(String numberTo);
+
+ /**
+ * Get the value for automatically forward all calls to the specified
+ * number using the same provider..
+ * @return numberTo number to use for forwarding
+ */
+ public String getCallForward();
+
+ /**
+ * Clear any previous settings.
+ */
+ public void clear();
+}
diff --git a/src/net/java/sip/communicator/util/swing/SIPCommDialog.java b/src/net/java/sip/communicator/util/swing/SIPCommDialog.java
index a9af2c4..511df08 100644
--- a/src/net/java/sip/communicator/util/swing/SIPCommDialog.java
+++ b/src/net/java/sip/communicator/util/swing/SIPCommDialog.java
@@ -424,15 +424,8 @@ public class SIPCommDialog
/**
* All functions implemented in this method will be invoked when user
* presses the Escape key.
- *
- * @param escaped <tt>true</tt> if this dialog has been closed by pressing
- * the Esc key; otherwise, <tt>false</tt>
- */
- /**
- * All functions implemented in this method will be invoked when user
- * presses the Escape key.
* @param isEscaped indicates if this frame has been closed by pressing the
- * Esc key
+ * Esc key; otherwise, <tt>false</tt>
*/
protected void close(boolean isEscaped)
{