mirror of https://github.com/sipwise/jitsi.git
Commits callRecording.patch and recordButton.png provided by Dmitri Melnikov on the dev mailing list in the thread "Call Recording".
parent
13dbadac72
commit
347408cc5b
|
After Width: | Height: | Size: 389 B |
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.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.swing.*;
|
||||
|
||||
/**
|
||||
* The base class for all toggle buttons which control the call from the UI.
|
||||
* Allows extending buttons to focus on performing their toggle actions.
|
||||
*
|
||||
* @author Dmitri Melnikov
|
||||
*/
|
||||
public abstract class AbstractCallToggleButton
|
||||
extends SIPCommToggleButton
|
||||
implements ActionListener
|
||||
{
|
||||
/**
|
||||
* The <tt>Call</tt> that this button controls.
|
||||
*/
|
||||
protected final Call call;
|
||||
|
||||
/**
|
||||
* Initializes a new <tt>AbstractCallToggleButton</tt> instance which is to
|
||||
* control a toggle action for a specific <tt>Call</tt>.
|
||||
*
|
||||
* @param call the <tt>Call</tt> to be controlled by the instance
|
||||
* @param fullScreen <tt>true</tt> if the new instance is to be used in
|
||||
* full-screen UI; otherwise, <tt>false</tt>
|
||||
* @param selected <tt>true</tt> if the new toggle button is to be initially
|
||||
* selected; otherwise, <tt>false</tt>
|
||||
* @param iconImageID the <tt>ImageID</tt> of the image to be used as the
|
||||
* icon of the new instance
|
||||
* @param toolTipTextKey the key in the <tt>ResourceManagementService</tt>
|
||||
* of the internationalized string which is to be used as the tool tip text
|
||||
* of the new instance
|
||||
*/
|
||||
public AbstractCallToggleButton(
|
||||
Call call,
|
||||
boolean fullScreen,
|
||||
boolean selected,
|
||||
ImageID iconImageID,
|
||||
String toolTipTextKey)
|
||||
{
|
||||
this.call = call;
|
||||
|
||||
ImageID bgImage;
|
||||
ImageID bgRolloverImage;
|
||||
ImageID pressedImage;
|
||||
|
||||
if (fullScreen)
|
||||
{
|
||||
bgImage = ImageLoader.FULL_SCREEN_BUTTON_BG;
|
||||
bgRolloverImage = ImageLoader.FULL_SCREEN_BUTTON_BG;
|
||||
pressedImage = ImageLoader.FULL_SCREEN_BUTTON_BG_PRESSED;
|
||||
}
|
||||
else
|
||||
{
|
||||
bgImage = ImageLoader.CALL_SETTING_BUTTON_BG;
|
||||
bgRolloverImage = ImageLoader.CALL_SETTING_BUTTON_BG;
|
||||
pressedImage = ImageLoader.CALL_SETTING_BUTTON_PRESSED_BG;
|
||||
}
|
||||
setBgImage(ImageLoader.getImage(bgImage));
|
||||
setBgRolloverImage(ImageLoader.getImage(bgRolloverImage));
|
||||
setPressedImage(ImageLoader.getImage(pressedImage));
|
||||
|
||||
setIconImage(ImageLoader.getImage(iconImageID));
|
||||
if (toolTipTextKey != null)
|
||||
{
|
||||
setToolTipText(
|
||||
GuiActivator.getResources().getI18NString(toolTipTextKey));
|
||||
}
|
||||
|
||||
addActionListener(this);
|
||||
setSelected(selected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies this <tt>AbstractCallToggleButton</tt> that its associated
|
||||
* action has been performed and that it should execute its very logic.
|
||||
*
|
||||
* @param evt an <tt>ActionEvent</tt> which describes the specifics of the
|
||||
* performed action
|
||||
*/
|
||||
public abstract void actionPerformed(ActionEvent evt);
|
||||
}
|
||||
@ -0,0 +1,351 @@
|
||||
/*
|
||||
* 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.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.io.*;
|
||||
import java.text.*;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import net.java.sip.communicator.impl.gui.*;
|
||||
import net.java.sip.communicator.impl.gui.utils.*;
|
||||
import net.java.sip.communicator.service.configuration.*;
|
||||
import net.java.sip.communicator.service.neomedia.*;
|
||||
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.swing.*;
|
||||
|
||||
/**
|
||||
* The button that starts/stops the call recording.
|
||||
*
|
||||
* @author Dmitri Melnikov
|
||||
*/
|
||||
public class RecordButton
|
||||
extends AbstractCallToggleButton
|
||||
{
|
||||
/**
|
||||
* Resource service.
|
||||
*/
|
||||
private static ResourceManagementService resources
|
||||
= GuiActivator.getResources();
|
||||
|
||||
/**
|
||||
* Configuration service.
|
||||
*/
|
||||
private static ConfigurationService configurationService
|
||||
= GuiActivator.getConfigurationService();
|
||||
|
||||
/**
|
||||
* The date format used in file names.
|
||||
*/
|
||||
private static SimpleDateFormat format
|
||||
= new SimpleDateFormat("yyyy-MM-dd@HH.mm.ss");
|
||||
|
||||
/**
|
||||
* <tt>true</tt> when the default directory to save calls to is set,
|
||||
* <tt>false</tt> otherwise.
|
||||
*/
|
||||
private boolean isCallDirSet = false;
|
||||
|
||||
/**
|
||||
* The full filename of the saved call on the file system.
|
||||
*/
|
||||
private String callFilename;
|
||||
|
||||
/**
|
||||
* Input panel.
|
||||
*/
|
||||
private InputPanel inputPanel;
|
||||
|
||||
/**
|
||||
* Initializes a new <tt>RecordButton</tt> instance which is to record the
|
||||
* audio stream.
|
||||
*
|
||||
* @param call the <tt>Call</tt> to be associated with the new instance and
|
||||
* to have the audio stream recorded
|
||||
*/
|
||||
public RecordButton(Call call)
|
||||
{
|
||||
this(call, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new <tt>RecordButton</tt> instance which is to record the
|
||||
* audio stream.
|
||||
*
|
||||
* @param call the <tt>Call</tt> to be associated with the new instance and
|
||||
* to have its audio stream recorded
|
||||
* @param fullScreen <tt>true</tt> if the new instance is to be used in
|
||||
* full-screen UI; otherwise, <tt>false</tt>
|
||||
* @param selected <tt>true</tt> if the new toggle button is to be initially
|
||||
* selected; otherwise, <tt>false</tt>
|
||||
*/
|
||||
public RecordButton(Call call, boolean fullScreen, boolean selected)
|
||||
{
|
||||
super(call, fullScreen, selected, ImageLoader.RECORD_BUTTON, null);
|
||||
|
||||
inputPanel = new InputPanel();
|
||||
|
||||
String toolTip
|
||||
= resources.getI18NString("service.gui.RECORD_BUTTON_TOOL_TIP");
|
||||
String saveDir
|
||||
= configurationService.getString(Recorder.SAVED_CALLS_PATH);
|
||||
if (saveDir != null)
|
||||
{
|
||||
isCallDirSet = true;
|
||||
toolTip = toolTip + " (" + saveDir + ")";
|
||||
}
|
||||
setToolTipText(toolTip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts/stops the recording of the call when this button is pressed.
|
||||
*
|
||||
* @param evt the <tt>ActionEvent</tt> that notified us of the action
|
||||
*/
|
||||
public void actionPerformed(ActionEvent evt)
|
||||
{
|
||||
if (call != null)
|
||||
{
|
||||
OperationSetBasicTelephony<?> telephony =
|
||||
call.getProtocolProvider().getOperationSet(
|
||||
OperationSetBasicTelephony.class);
|
||||
|
||||
boolean isRecordSelected = isSelected();
|
||||
// start recording
|
||||
if (isRecordSelected)
|
||||
{
|
||||
// ask user input about where to save the call
|
||||
if (!isCallDirSet)
|
||||
{
|
||||
int status =
|
||||
JOptionPane
|
||||
.showConfirmDialog(
|
||||
this,
|
||||
inputPanel,
|
||||
resources
|
||||
.getI18NString("plugin.callrecordingconfig.SAVE_CALL"),
|
||||
JOptionPane.OK_CANCEL_OPTION,
|
||||
JOptionPane.QUESTION_MESSAGE);
|
||||
if (status == JOptionPane.OK_OPTION)
|
||||
{
|
||||
callFilename = inputPanel.getSelectedFilename();
|
||||
configurationService.setProperty(Recorder.CALL_FORMAT,
|
||||
inputPanel.getSelectedFormat());
|
||||
}
|
||||
else
|
||||
{
|
||||
// user canceled the recording
|
||||
setSelected(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
callFilename = createDefaultFilename();
|
||||
|
||||
telephony.startRecording(call, callFilename);
|
||||
}
|
||||
// stop recording
|
||||
else
|
||||
{
|
||||
telephony.stopRecording(call);
|
||||
JOptionPane.showMessageDialog(this,
|
||||
resources.getI18NString(
|
||||
"plugin.callrecordingconfig.CALL_SAVED_TO", new String[]
|
||||
{ callFilename }),
|
||||
resources
|
||||
.getI18NString("plugin.callrecordingconfig.CALL_SAVED"),
|
||||
JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a full filename for the call by combining the directory, file
|
||||
* prefix and extension. If the directory is <tt>null</tt> user's home
|
||||
* directory is used.
|
||||
*
|
||||
* @return a full filename for the call
|
||||
*/
|
||||
private String createDefaultFilename()
|
||||
{
|
||||
String callsDir
|
||||
= configurationService.getString(Recorder.SAVED_CALLS_PATH);
|
||||
|
||||
// set to user's home when null
|
||||
if (callsDir == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
callsDir
|
||||
= GuiActivator
|
||||
.getFileAccessService()
|
||||
.getDefaultDownloadDirectory()
|
||||
.getAbsolutePath();
|
||||
}
|
||||
catch (IOException ioex)
|
||||
{
|
||||
// Leave it in the current directory.
|
||||
}
|
||||
}
|
||||
|
||||
String ext = configurationService.getString(Recorder.CALL_FORMAT);
|
||||
|
||||
if (ext == null)
|
||||
ext = SoundFileUtils.mp2;
|
||||
|
||||
return
|
||||
((callsDir == null) ? "" : (callsDir + File.separator))
|
||||
+ generateCallFilename(ext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a file name for the call based on the current date.
|
||||
*
|
||||
* @param ext file extension
|
||||
* @return the file name for the call
|
||||
*/
|
||||
private String generateCallFilename(String ext)
|
||||
{
|
||||
return format.format(new Date()) + "-confcall." + ext;
|
||||
}
|
||||
|
||||
private static class InputPanel
|
||||
extends TransparentPanel
|
||||
{
|
||||
/**
|
||||
* Call file chooser.
|
||||
*/
|
||||
private SipCommFileChooser callFileChooser;
|
||||
|
||||
/**
|
||||
* Selected file.
|
||||
*/
|
||||
private String selectedFilename;
|
||||
|
||||
/**
|
||||
* Format combo box.
|
||||
*/
|
||||
private JComboBox formatComboBox;
|
||||
|
||||
/**
|
||||
* Builds the panel.
|
||||
*/
|
||||
public InputPanel()
|
||||
{
|
||||
super(new BorderLayout());
|
||||
|
||||
initComponents();
|
||||
|
||||
callFileChooser =
|
||||
GenericFileDialog.create(null, resources
|
||||
.getI18NString("plugin.callrecordingconfig.SAVE_CALL"),
|
||||
SipCommFileChooser.SAVE_FILE_OPERATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selected file.
|
||||
*
|
||||
* @return the selected file
|
||||
*/
|
||||
public String getSelectedFilename()
|
||||
{
|
||||
return selectedFilename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selected format.
|
||||
*
|
||||
* @return the selected format
|
||||
*/
|
||||
public String getSelectedFormat()
|
||||
{
|
||||
return (String) formatComboBox.getSelectedItem();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the UI components.
|
||||
*/
|
||||
private void initComponents()
|
||||
{
|
||||
JPanel labelsPanel = new TransparentPanel(new GridLayout(2, 1));
|
||||
JLabel formatLabel =
|
||||
new JLabel(resources
|
||||
.getI18NString("plugin.callrecordingconfig.FORMAT"));
|
||||
JLabel locationLabel =
|
||||
new JLabel(resources
|
||||
.getI18NString("plugin.callrecordingconfig.LOCATION"));
|
||||
labelsPanel.add(formatLabel);
|
||||
labelsPanel.add(locationLabel);
|
||||
|
||||
JPanel dirPanel =
|
||||
new TransparentPanel(new FlowLayout(FlowLayout.LEFT));
|
||||
final JTextField callDirTextField = new JTextField();
|
||||
callDirTextField.setPreferredSize(new Dimension(200, 30));
|
||||
callDirTextField.setEditable(false);
|
||||
dirPanel.add(callDirTextField);
|
||||
JButton callDirChooseButton =
|
||||
new JButton(new ImageIcon(resources
|
||||
.getImageInBytes("plugin.notificationconfig.FOLDER_ICON")));
|
||||
callDirChooseButton.setMinimumSize(new Dimension(30, 30));
|
||||
callDirChooseButton.setPreferredSize(new Dimension(30, 30));
|
||||
callDirChooseButton.addActionListener(new ActionListener()
|
||||
{
|
||||
public void actionPerformed(ActionEvent arg0)
|
||||
{
|
||||
File selectedFile = callFileChooser.getFileFromDialog();
|
||||
|
||||
if (selectedFile != null)
|
||||
{
|
||||
selectedFilename = selectedFile.getAbsolutePath();
|
||||
callDirTextField.setText(selectedFilename);
|
||||
}
|
||||
}
|
||||
});
|
||||
dirPanel.add(callDirChooseButton);
|
||||
|
||||
JPanel comboPanel =
|
||||
new TransparentPanel(new FlowLayout(FlowLayout.LEFT));
|
||||
JLabel emptyLabel = new JLabel();
|
||||
emptyLabel.setPreferredSize(new Dimension(30, 30));
|
||||
comboPanel.add(createFormatsComboBox());
|
||||
comboPanel.add(emptyLabel);
|
||||
|
||||
JPanel valuesPanel = new TransparentPanel(new GridLayout(2, 1));
|
||||
valuesPanel.add(comboPanel);
|
||||
valuesPanel.add(dirPanel);
|
||||
|
||||
this.add(labelsPanel, BorderLayout.WEST);
|
||||
this.add(valuesPanel, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a combo box with supported audio formats.
|
||||
*
|
||||
* @return a combo box with supported audio formats
|
||||
*/
|
||||
private Component createFormatsComboBox()
|
||||
{
|
||||
ComboBoxModel formatsComboBoxModel =
|
||||
new DefaultComboBoxModel(
|
||||
new String[] {
|
||||
SoundFileUtils.mp2,
|
||||
SoundFileUtils.wav,
|
||||
SoundFileUtils.au,
|
||||
SoundFileUtils.aif,
|
||||
SoundFileUtils.gsm });
|
||||
|
||||
formatComboBox = new JComboBox();
|
||||
formatComboBox.setPreferredSize(new Dimension(200, 30));
|
||||
formatComboBox.setModel(formatsComboBoxModel);
|
||||
return formatComboBox;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,266 @@
|
||||
/*
|
||||
* 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.neomedia;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.io.*;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import net.java.sip.communicator.service.configuration.*;
|
||||
import net.java.sip.communicator.service.neomedia.*;
|
||||
import net.java.sip.communicator.service.resources.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
import net.java.sip.communicator.util.swing.*;
|
||||
|
||||
/**
|
||||
* The saved calls management and configuration form.
|
||||
*
|
||||
* @author Dmitri Melnikov
|
||||
*/
|
||||
public class CallRecordingConfigForm
|
||||
extends TransparentPanel
|
||||
implements ActionListener
|
||||
{
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private final Logger logger
|
||||
= Logger.getLogger(CallRecordingConfigForm.class);
|
||||
|
||||
/**
|
||||
* The resource service.
|
||||
*/
|
||||
private static final ResourceManagementService resources
|
||||
= NeomediaActivator.getResources();
|
||||
|
||||
/**
|
||||
* Directory where calls are stored. Default is SC_HOME/calls.
|
||||
*/
|
||||
private String savedCallsDir;
|
||||
|
||||
/**
|
||||
* Directory choose dialog.
|
||||
*/
|
||||
private SipCommFileChooser dirChooser;
|
||||
|
||||
/**
|
||||
* UI components.
|
||||
*/
|
||||
private JButton callDirChooseButton;
|
||||
private JTextField callDirTextField;
|
||||
private JComboBox formatsComboBox;
|
||||
private JCheckBox saveCallsToCheckBox;
|
||||
|
||||
/**
|
||||
* Creates an instance of the <tt>CallConfigurationPanel</tt>.
|
||||
* Checks for the <tt>SAVED_CALLS_PATH</tt> and sets it if it does not
|
||||
* exist.
|
||||
*/
|
||||
public CallRecordingConfigForm()
|
||||
{
|
||||
super(new BorderLayout());
|
||||
|
||||
initComponents();
|
||||
loadValues();
|
||||
|
||||
dirChooser =
|
||||
GenericFileDialog.create(null, resources
|
||||
.getI18NString("plugin.callrecordingconfig.CHOOSE_DIR"),
|
||||
SipCommFileChooser.LOAD_FILE_OPERATION);
|
||||
((JFileChooser) dirChooser)
|
||||
.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads values from the configuration and sets the UI components to these
|
||||
* values.
|
||||
*/
|
||||
private void loadValues()
|
||||
{
|
||||
ConfigurationService configurationService
|
||||
= NeomediaActivator.getConfigurationService();
|
||||
|
||||
String callFormat = configurationService.getString(Recorder.CALL_FORMAT);
|
||||
formatsComboBox.setSelectedItem(callFormat == null ? SoundFileUtils.mp2
|
||||
: callFormat);
|
||||
|
||||
savedCallsDir = configurationService.getString(Recorder.SAVED_CALLS_PATH);
|
||||
saveCallsToCheckBox.setSelected(savedCallsDir != null);
|
||||
callDirTextField.setText(savedCallsDir);
|
||||
callDirTextField.setEnabled(saveCallsToCheckBox.isSelected());
|
||||
callDirChooseButton.setEnabled(saveCallsToCheckBox.isSelected());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a panel with call management components.
|
||||
*/
|
||||
private void initComponents()
|
||||
{
|
||||
// labels panel
|
||||
JPanel labelsPanel = new TransparentPanel(new GridLayout(2, 1));
|
||||
|
||||
JLabel formatsLabel = new JLabel(
|
||||
resources.getI18NString("plugin.callrecordingconfig.SUPPORTED_FORMATS"));
|
||||
saveCallsToCheckBox =
|
||||
new SIPCommCheckBox(resources
|
||||
.getI18NString("plugin.callrecordingconfig.SAVE_CALLS"));
|
||||
saveCallsToCheckBox.addActionListener(this);
|
||||
|
||||
labelsPanel.add(formatsLabel);
|
||||
labelsPanel.add(saveCallsToCheckBox);
|
||||
|
||||
// combo box panel
|
||||
JPanel comboPanel =
|
||||
new TransparentPanel(new FlowLayout(FlowLayout.LEFT));
|
||||
|
||||
JLabel emptyLabel = new JLabel();
|
||||
emptyLabel.setPreferredSize(new Dimension(30, 30));
|
||||
comboPanel.add(createFormatsComboBox());
|
||||
comboPanel.add(emptyLabel);
|
||||
|
||||
// saved calls directory panel
|
||||
JPanel callDirPanel =
|
||||
new TransparentPanel(new FlowLayout(FlowLayout.LEFT));
|
||||
|
||||
callDirTextField = new JTextField();
|
||||
callDirTextField.setPreferredSize(new Dimension(200, 30));
|
||||
callDirTextField.addActionListener(this);
|
||||
callDirPanel.add(callDirTextField);
|
||||
|
||||
callDirChooseButton =
|
||||
new JButton(new ImageIcon(resources
|
||||
.getImageInBytes("plugin.notificationconfig.FOLDER_ICON")));
|
||||
callDirChooseButton.setMinimumSize(new Dimension(30,30));
|
||||
callDirChooseButton.setPreferredSize(new Dimension(30,30));
|
||||
callDirChooseButton.addActionListener(this);
|
||||
callDirPanel.add(callDirChooseButton);
|
||||
|
||||
// values panel
|
||||
JPanel valuesPanel = new TransparentPanel(new GridLayout(2, 1));
|
||||
valuesPanel.add(comboPanel);
|
||||
valuesPanel.add(callDirPanel);
|
||||
|
||||
// main panel
|
||||
JPanel mainPanel = new TransparentPanel(new BorderLayout());
|
||||
mainPanel.add(labelsPanel, BorderLayout.WEST);
|
||||
mainPanel.add(valuesPanel, BorderLayout.CENTER);
|
||||
|
||||
this.add(mainPanel, BorderLayout.NORTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a combo box with supported audio formats.
|
||||
*
|
||||
* @return a combo box with supported audio formats
|
||||
*/
|
||||
private Component createFormatsComboBox()
|
||||
{
|
||||
ComboBoxModel formatsComboBoxModel =
|
||||
new DefaultComboBoxModel(
|
||||
new String[] {
|
||||
SoundFileUtils.mp2,
|
||||
SoundFileUtils.wav,
|
||||
SoundFileUtils.au,
|
||||
SoundFileUtils.aif,
|
||||
SoundFileUtils.gsm });
|
||||
|
||||
formatsComboBox = new JComboBox();
|
||||
formatsComboBox.setPreferredSize(new Dimension(200, 30));
|
||||
formatsComboBox.setModel(formatsComboBoxModel);
|
||||
|
||||
formatsComboBox.addItemListener(new ItemListener()
|
||||
{
|
||||
public void itemStateChanged(ItemEvent event)
|
||||
{
|
||||
if (event.getStateChange() == ItemEvent.SELECTED)
|
||||
NeomediaActivator
|
||||
.getConfigurationService()
|
||||
.setProperty(Recorder.CALL_FORMAT, event.getItem());
|
||||
}
|
||||
});
|
||||
return formatsComboBox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that one of the contained in this panel components has
|
||||
* performed an action.
|
||||
*
|
||||
* @param e the <tt>ActionEvent</tt> that notified us
|
||||
*/
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
Object source = e.getSource();
|
||||
if (source == saveCallsToCheckBox)
|
||||
{
|
||||
boolean selected = saveCallsToCheckBox.isSelected();
|
||||
callDirTextField.setEnabled(selected);
|
||||
callDirChooseButton.setEnabled(selected);
|
||||
if (selected)
|
||||
{
|
||||
// set default directory
|
||||
try
|
||||
{
|
||||
changeCallsDir(
|
||||
NeomediaActivator
|
||||
.getFileAccessService()
|
||||
.getDefaultDownloadDirectory());
|
||||
}
|
||||
catch (IOException ioex)
|
||||
{
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove default directory prop
|
||||
NeomediaActivator
|
||||
.getConfigurationService()
|
||||
.setProperty(Recorder.SAVED_CALLS_PATH, null);
|
||||
callDirTextField.setText(null);
|
||||
}
|
||||
}
|
||||
else if (source == callDirChooseButton)
|
||||
{
|
||||
File newDir = dirChooser.getFileFromDialog();
|
||||
changeCallsDir(newDir);
|
||||
}
|
||||
else if (source == callDirTextField)
|
||||
{
|
||||
File newDir = new File(callDirTextField.getText());
|
||||
changeCallsDir(newDir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the new directory for the saved calls to <tt>dir</tt>.
|
||||
*
|
||||
* @param dir the new chosen directory
|
||||
* @return <tt>true</tt> if directory was changed successfully,
|
||||
* <tt>false</tt> otherwise
|
||||
*/
|
||||
private boolean changeCallsDir(File dir)
|
||||
{
|
||||
if (dir != null && dir.isDirectory())
|
||||
{
|
||||
savedCallsDir = dir.getAbsolutePath();
|
||||
callDirTextField.setText(savedCallsDir);
|
||||
NeomediaActivator
|
||||
.getConfigurationService()
|
||||
.setProperty(Recorder.SAVED_CALLS_PATH, savedCallsDir);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Calls directory changed to " + savedCallsDir);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Calls directory not changed.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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.neomedia;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import javax.media.*;
|
||||
import javax.media.protocol.*;
|
||||
|
||||
import net.java.sip.communicator.impl.neomedia.device.*;
|
||||
import net.java.sip.communicator.service.configuration.*;
|
||||
import net.java.sip.communicator.service.neomedia.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
/**
|
||||
* The call recording implementation.
|
||||
* Provides the capability to start and stop call recording.
|
||||
*
|
||||
* @author Dmitri Melnikov
|
||||
*/
|
||||
public class RecorderImpl
|
||||
implements Recorder
|
||||
{
|
||||
/**
|
||||
* The <tt>Logger</tt> used by the <tt>RecorderImpl</tt> class and its
|
||||
* instances for logging output.
|
||||
*/
|
||||
private static final Logger logger = Logger.getLogger(RecorderImpl.class);
|
||||
|
||||
/**
|
||||
* The <tt>MediaDeviceSession</tt> is used to create an output data source.
|
||||
*/
|
||||
private MediaDeviceSession deviceSession;
|
||||
|
||||
/**
|
||||
* <tt>DataSink</tt> used to save the output data.
|
||||
*/
|
||||
private DataSink sink;
|
||||
|
||||
/**
|
||||
* <tt>true</tt> if recording was started, <tt>false</tt>
|
||||
* otherwise.
|
||||
*/
|
||||
private boolean recording = false;
|
||||
|
||||
/**
|
||||
* Constructs the <tt>RecorderImpl</tt> with the provided session.
|
||||
*
|
||||
* @param device device that can create a session that provides the output
|
||||
* data source
|
||||
*/
|
||||
public RecorderImpl(AudioMixerMediaDevice device)
|
||||
{
|
||||
if (device == null)
|
||||
throw new NullPointerException("device");
|
||||
|
||||
ConfigurationService configurationService
|
||||
= NeomediaActivator.getConfigurationService();
|
||||
String format = configurationService.getString(Recorder.CALL_FORMAT);
|
||||
|
||||
if (format == null)
|
||||
format = SoundFileUtils.mp2;
|
||||
deviceSession
|
||||
= device.createRecordingSession(getContentDescriptor(format));
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the call recording.
|
||||
*
|
||||
* @param filename call filename, when <tt>null</tt> a default filename is
|
||||
* used
|
||||
*/
|
||||
public void startRecording(String filename)
|
||||
{
|
||||
if (!recording)
|
||||
{
|
||||
if (filename == null)
|
||||
throw new NullPointerException("filename");
|
||||
|
||||
DataSource outputDataSource = deviceSession.getOutputDataSource();
|
||||
|
||||
try
|
||||
{
|
||||
sink
|
||||
= Manager.createDataSink(
|
||||
outputDataSource,
|
||||
new MediaLocator("file:" + filename));
|
||||
sink.open();
|
||||
sink.start();
|
||||
}
|
||||
catch (NoDataSinkException ndsex)
|
||||
{
|
||||
logger.error("No datasink can be found", ndsex);
|
||||
}
|
||||
catch (IOException ioex)
|
||||
{
|
||||
logger.error("Writing to datasink failed", ioex);
|
||||
}
|
||||
|
||||
recording = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the call recording.
|
||||
*/
|
||||
public void stopRecording()
|
||||
{
|
||||
if (recording)
|
||||
{
|
||||
deviceSession.close();
|
||||
deviceSession = null;
|
||||
|
||||
if (sink != null)
|
||||
{
|
||||
sink.close();
|
||||
sink = null;
|
||||
}
|
||||
|
||||
recording = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a content descriptor to create a recording session with.
|
||||
*
|
||||
* @param format the format that corresponding to the content descriptor
|
||||
* @return content descriptor
|
||||
*/
|
||||
private ContentDescriptor getContentDescriptor(String format)
|
||||
{
|
||||
String type = FileTypeDescriptor.MPEG_AUDIO;
|
||||
|
||||
if (SoundFileUtils.wav.equals(format))
|
||||
type = FileTypeDescriptor.WAVE;
|
||||
else if (SoundFileUtils.gsm.equals(format))
|
||||
type = FileTypeDescriptor.GSM;
|
||||
else if (SoundFileUtils.au.equals(format))
|
||||
type = FileTypeDescriptor.BASIC_AUDIO;
|
||||
else if (SoundFileUtils.aif.equals(format))
|
||||
type = FileTypeDescriptor.AIFF;
|
||||
|
||||
return new ContentDescriptor(type);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.service.neomedia;
|
||||
|
||||
|
||||
/**
|
||||
* The call recording interface.
|
||||
* Provides the capability to start and stop call recording.
|
||||
*
|
||||
* @author Dmitri Melnikov
|
||||
*/
|
||||
public interface Recorder
|
||||
{
|
||||
/**
|
||||
* Configuration property for the full path to the directory with saved
|
||||
* calls.
|
||||
*/
|
||||
public static final String SAVED_CALLS_PATH =
|
||||
"net.java.sip.communicator.impl.neomedia.SAVED_CALLS_PATH";
|
||||
/**
|
||||
* Configuration property format of the saved call.
|
||||
*/
|
||||
public static final String CALL_FORMAT =
|
||||
"net.java.sip.communicator.impl.neomedia.CALL_FORMAT";
|
||||
|
||||
/**
|
||||
* Starts the call recording.
|
||||
*
|
||||
* @param callFilename call filename
|
||||
*/
|
||||
public void startRecording(String callFilename);
|
||||
|
||||
/**
|
||||
* Stops the call recording.
|
||||
*/
|
||||
public void stopRecording();
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* Defines the different permit extension file.
|
||||
*
|
||||
* @author Alexandre Maillard
|
||||
* @author Dmitri Melnikov
|
||||
*/
|
||||
public class SoundFileUtils
|
||||
{
|
||||
/**
|
||||
* Different extension of a sound file
|
||||
*/
|
||||
public final static String wav = "wav";
|
||||
public final static String mid = "midi";
|
||||
public final static String mp2 = "mp2";
|
||||
public final static String mp3 = "mp3";
|
||||
public final static String mod = "mod";
|
||||
public final static String ram = "ram";
|
||||
public final static String wma = "wma";
|
||||
public final static String ogg = "ogg";
|
||||
public final static String gsm = "gsm";
|
||||
public final static String aif = "aiff";
|
||||
public final static String au = "au";
|
||||
|
||||
/**
|
||||
* Checks whether this file is a sound file.
|
||||
*
|
||||
* @param f <tt>File</tt> to check
|
||||
* @return <tt>true</tt> if it's a sound file, <tt>false</tt> otherwise
|
||||
*/
|
||||
public static boolean isSoundFile(File f)
|
||||
{
|
||||
String extension = SoundFileUtils.getExtension(f);
|
||||
if (extension != null)
|
||||
{
|
||||
return extension.equals(SoundFileUtils.wav) ||
|
||||
extension.equals(SoundFileUtils.mid) ||
|
||||
extension.equals(SoundFileUtils.mp2) ||
|
||||
extension.equals(SoundFileUtils.mp3) ||
|
||||
extension.equals(SoundFileUtils.mod) ||
|
||||
extension.equals(SoundFileUtils.ogg) ||
|
||||
extension.equals(SoundFileUtils.wma) ||
|
||||
extension.equals(SoundFileUtils.gsm) ||
|
||||
extension.equals(SoundFileUtils.au) ||
|
||||
extension.equals(SoundFileUtils.ram);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file extension.
|
||||
* TODO: There are at least 2 other methods like this scattered around
|
||||
* the SC code, we should move them all to util package.
|
||||
*
|
||||
* @param f which wants the extension
|
||||
* @return Return the extension as a String
|
||||
*/
|
||||
private static String getExtension(File f)
|
||||
{
|
||||
String ext = null;
|
||||
String s = f.getName();
|
||||
int i = s.lastIndexOf('.');
|
||||
|
||||
if (i > 0 && i < s.length() - 1)
|
||||
ext = s.substring(i+1).toLowerCase();
|
||||
|
||||
return ext;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue