diff options
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) { |