- Fixes call recording-related issues reported off-list by Emil Ivov.

- Adds javadocs, fixes warnings.
cusax-fix
Lyubomir Marinov 16 years ago
parent 21c58834fb
commit d613851fa2

@ -137,6 +137,27 @@ public void actionPerformed(ActionEvent evt)
}
setSelected(startedRecording);
}
/*
* Notify the user the call has been successfully saved upon the
* recorder stopping.
*/
if (startedRecording && (recorder != null))
{
recorder.addListener(
new Recorder.Listener()
{
public void recorderStopped(Recorder recorder)
{
NotificationManager.fireNotification(
NotificationManager.CALL_SAVED,
resources.getI18NString(
"plugin.callrecordingconfig.CALL_SAVED"),
resources.getI18NString(
"plugin.callrecordingconfig.CALL_SAVED_TO",
new String[] { callFilename }));
}
});
}
}
// stop recording
else if (recorder != null)
@ -144,13 +165,6 @@ else if (recorder != null)
try
{
recorder.stop();
NotificationManager.fireNotification(
NotificationManager.CALL_SAVED,
resources.getI18NString(
"plugin.callrecordingconfig.CALL_SAVED"),
resources.getI18NString(
"plugin.callrecordingconfig.CALL_SAVED_TO",
new String[] { callFilename }));
}
finally
{
@ -189,7 +203,7 @@ private String createDefaultFilename(String savedCallsPath)
}
}
String ext = configuration.getString(Recorder.CALL_FORMAT);
String ext = configuration.getString(Recorder.FORMAT);
// Use a default format when the configured one seems invalid.
if ((ext == null)
@ -298,6 +312,7 @@ private boolean startRecording()
{
String savedCallsPath
= configuration.getString(Recorder.SAVED_CALLS_PATH);
String callFormat;
// Ask the user where to save the call.
if ((savedCallsPath == null) || (savedCallsPath.length() == 0))
@ -381,7 +396,7 @@ public String getDescription()
* OS X at least) i.e. no format, then it is not obvious that we
* have to override the set Recorder.CALL_FORMAT.
*/
String callFormat = SoundFileUtils.getExtension(selectedFile);
callFormat = SoundFileUtils.getExtension(selectedFile);
if ((callFormat != null) && (callFormat.length() != 0))
{
@ -407,7 +422,7 @@ public String getDescription()
= SoundFileUtils.DEFAULT_CALL_RECORDING_FORMAT;
callFilename += '.' + callFormat;
}
configuration.setProperty(Recorder.CALL_FORMAT, callFormat);
configuration.setProperty(Recorder.FORMAT, callFormat);
}
}
else
@ -417,7 +432,10 @@ public String getDescription()
}
}
else
{
callFilename = createDefaultFilename(savedCallsPath);
callFormat = SoundFileUtils.getExtension(new File(callFilename));
}
Throwable exception = null;
@ -426,7 +444,12 @@ public String getDescription()
Recorder recorder = getRecorder();
if (recorder != null)
recorder.start(callFilename);
{
if ((callFormat == null) || (callFormat.length() <= 0))
callFormat = SoundFileUtils.DEFAULT_CALL_RECORDING_FORMAT;
recorder.start(callFormat, callFilename);
}
this.recorder = recorder;
}

@ -46,6 +46,10 @@ public class ChatConversationPanel
MouseListener,
ClipboardOwner
{
/**
* The <tt>Logger</tt> used by the <tt>ChatConversationPanel</tt> class and
* its instances for logging output.
*/
private static final Logger logger
= Logger.getLogger(ChatConversationPanel.class);
@ -335,7 +339,8 @@ public void setBounds(int x, int y, int width, int height)
* Processes the message given by the parameters.
*
* @param chatMessage the message
* @param keyword
* @param keyword a substring of <tt>chatMessage</tt> to be highlighted upon
* display of <tt>chatMessage</tt> in the UI
* @return the processed message
*/
public String processMessage(ChatMessage chatMessage, String keyword)
@ -1266,12 +1271,13 @@ private class MyTextPane
* @param event the <tt>MouseEvent</tt>
* @return the string to be used as the tooltip for <i>event</i>.
*/
@Override
public String getToolTipText(MouseEvent event)
{
if(currentHref != null && currentHref.length() != 0)
return currentHref;
else
return null;
return
((currentHref != null) && (currentHref.length() != 0))
? currentHref
: null;
}
}

@ -88,18 +88,16 @@ public CallRecordingConfigForm()
*/
private void loadValues()
{
ConfigurationService configurationService
ConfigurationService configuration
= NeomediaActivator.getConfigurationService();
String callFormat
= configurationService.getString(Recorder.CALL_FORMAT);
String format = configuration.getString(Recorder.FORMAT);
formatsComboBox.setSelectedItem(
(callFormat == null)
(format == null)
? SoundFileUtils.DEFAULT_CALL_RECORDING_FORMAT
: callFormat);
: format);
savedCallsDir
= configurationService.getString(Recorder.SAVED_CALLS_PATH);
savedCallsDir = configuration.getString(Recorder.SAVED_CALLS_PATH);
saveCallsToCheckBox.setSelected(savedCallsDir != null);
callDirTextField.setText(savedCallsDir);
callDirTextField.setEnabled(saveCallsToCheckBox.isSelected());
@ -192,7 +190,7 @@ public void itemStateChanged(ItemEvent event)
{
NeomediaActivator
.getConfigurationService()
.setProperty(Recorder.CALL_FORMAT, event.getItem());
.setProperty(Recorder.FORMAT, event.getItem());
}
}
});

@ -13,7 +13,6 @@
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.service.neomedia.MediaException; // disambiguation
import net.java.sip.communicator.util.*;
@ -28,11 +27,6 @@
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 list of formats in which <tt>RecorderImpl</tt> instances support
@ -48,16 +42,23 @@ public class RecorderImpl
SoundFileUtils.wav
};
/**
* The <tt>AudioMixerMediaDevice</tt> which is to be or which is already
* being recorded by this <tt>Recorder</tt>.
*/
private final AudioMixerMediaDevice device;
/**
* The <tt>MediaDeviceSession</tt> is used to create an output data source.
*/
private MediaDeviceSession deviceSession;
/**
* The format of {@link #deviceSession} in particular and of the recording
* produced by this <tt>Recorder</tt> in general.
* The <tt>List</tt> of <tt>Recorder.Listener</tt>s interested in
* notifications from this <tt>Recorder</tt>.
*/
private final String format;
private final List<Recorder.Listener> listeners
= new ArrayList<Recorder.Listener>();
/**
* <tt>DataSink</tt> used to save the output data.
@ -75,39 +76,27 @@ public RecorderImpl(AudioMixerMediaDevice device)
if (device == null)
throw new NullPointerException("device");
ConfigurationService configuration
= NeomediaActivator.getConfigurationService();
String format = configuration.getString(Recorder.CALL_FORMAT);
this.device = device;
}
if (format == null)
format = SoundFileUtils.DEFAULT_CALL_RECORDING_FORMAT;
/**
* Adds a new <tt>Recorder.Listener</tt> to the list of listeners interested
* in notifications from this <tt>Recorder</tt>.
*
* @param listener the new <tt>Recorder.Listener</tt> to be added to the
* list of listeners interested in notifications from this <tt>Recorder</tt>
* @see Recorder#addListener(Recorder.Listener)
*/
public void addListener(Recorder.Listener listener)
{
if (listener == null)
throw new NullPointerException("listener");
try
{
deviceSession
= device.createRecordingSession(getContentDescriptor(
format));
}
catch (IllegalArgumentException iaex)
synchronized (listeners)
{
//seems like we had an illegal format stored in the configuration
//service
logger.debug(
"Unable to crate a "
+ format
+ " record. Will retry default format");
format = SoundFileUtils.DEFAULT_CALL_RECORDING_FORMAT;
//make sure we don't try the faulty format again.
configuration.setProperty(Recorder.CALL_FORMAT, null);
deviceSession
= device.createRecordingSession(getContentDescriptor(
format));
if (!listeners.contains(listener))
listeners.add(listener);
}
this.format = format;
}
/**
@ -156,25 +145,49 @@ public List<String> getSupportedFormats()
return Arrays.asList(SUPPORTED_FORMATS);
}
/**
* Removes a existing <tt>Recorder.Listener</tt> from the list of listeners
* interested in notifications from this <tt>Recorder</tt>.
*
* @param listener the existing <tt>Recorder.Listener</tt> to be removed
* from the list of listeners interested in notifications from this
* <tt>Recorder</tt>
* @see Recorder#removeListener(Recorder.Listener)
*/
public void removeListener(Recorder.Listener listener)
{
if (listener != null)
{
synchronized (listeners)
{
listeners.remove(listener);
}
}
}
/**
* Starts the recording of the media associated with this <tt>Recorder</tt>
* (e.g. the media being sent and received in a <tt>Call</tt>) into a file
* with a specific name.
*
* @param format the format into which the media associated with this
* <tt>Recorder</tt> is to be recorded into the specified file
* @param filename the name of the file into which the media associated with
* this <tt>Recorder</tt> is to be recorded
* @throws IOException if anything goes wrong with the input and/or output
* performed by this <tt>Recorder</tt>
* @throws MediaException if anything else goes wrong while starting the
* recording of media performed by this <tt>Recorder</tt>
* @see Recorder#start(String)
* @see Recorder#start(String, String)
*/
public void start(String filename)
public void start(String format, String filename)
throws IOException,
MediaException
{
if (this.sink == null)
{
if (format == null)
throw new NullPointerException("format");
if (filename == null)
throw new NullPointerException("filename");
@ -186,18 +199,48 @@ public void start(String filename)
int extensionBeginIndex = filename.lastIndexOf('.');
if (extensionBeginIndex < 0)
filename += '.' + this.format;
filename += '.' + format;
else if (extensionBeginIndex == filename.length() - 1)
filename += this.format;
filename += format;
MediaDeviceSession deviceSession = device.createSession();
try
{
deviceSession.setContentDescriptor(getContentDescriptor(format));
/*
* This RecorderImpl will use deviceSession to get a hold of the
* media being set to the remote peers associated with the same
* AudioMixerMediaDevice i.e. this RecorderImpl needs
* deviceSession to only capture and not play back.
*/
deviceSession.start(MediaDirection.SENDONLY);
this.deviceSession = deviceSession;
}
finally
{
if (this.deviceSession == null)
{
throw new MediaException(
"Failed to create MediaDeviceSession from"
+ " AudioMixerMediaDevice for the purposes of"
+ " recording");
}
}
DataSource outputDataSource = deviceSession.getOutputDataSource();
Throwable exception = null;
try
{
DataSource outputDataSource
= deviceSession.getOutputDataSource();
DataSink sink
= Manager.createDataSink(
outputDataSource,
new MediaLocator("file:" + filename));
sink.open();
sink.start();
@ -205,10 +248,18 @@ else if (extensionBeginIndex == filename.length() - 1)
}
catch (NoDataSinkException ndsex)
{
throw
new MediaException(
exception = ndsex;
}
finally
{
if ((this.sink == null) || (exception != null))
{
stop();
throw new MediaException(
"Failed to start recording into file " + filename,
ndsex);
exception);
}
}
}
}
@ -232,6 +283,23 @@ public void stop()
{
sink.close();
sink = null;
/*
* RecorderImpl creates the sink upon start() and it does it only if
* it is null so this RecorderImpl has really stopped only if it has
* managed to close() the (existing) sink. Notify the registered
* listeners.
*/
Recorder.Listener[] listeners;
synchronized (this.listeners)
{
listeners
= this.listeners.toArray(
new Recorder.Listener[this.listeners.size()]);
}
for (Recorder.Listener listener : listeners)
listener.recorderStopped(this);
}
}
}

@ -205,83 +205,6 @@ public synchronized MediaDeviceSession createSession()
return new MediaStreamMediaDeviceSession(deviceSession);
}
/**
* Create a new recording session.
*
* @param contentDescriptor the content descriptor for the session.
* @return a new <tt>MediaDeviceSession</tt>
*/
public synchronized MediaDeviceSession createRecordingSession(
final ContentDescriptor contentDescriptor)
{
if (deviceSession == null)
deviceSession = new AudioMixerMediaDeviceSession();
return new MediaStreamMediaDeviceSession(deviceSession)
{
/**
* Starts a specific <tt>Processor</tt> if this
* <tt>MediaDeviceSession</tt> has been started and the specified
* <tt>Processor</tt> is not started. Does not check the
* <tt>MediaDirection</tt> of this session when starting.
*
* @param processor the <tt>Processor</tt> to start
*/
@Override
protected void startProcessorInAccordWithDirection(
Processor processor)
{
if (processor.getState() != Processor.Started)
{
processor.start();
if (logger.isTraceEnabled())
{
logger.trace(
"Started Processor with hashCode "
+ processor.hashCode());
}
}
}
/**
* Overrides the method to set the processor's content descriptor
* to <tt>FileTypeDescriptor.MPEG_AUDIO</tt>.
*
* @param event the <tt>ControllerEvent</tt> specifying the
* <tt>Controller</tt> which is the source of the event and the very
* type of the event
*/
@Override
protected void processorControllerUpdate(ControllerEvent event)
{
super.processorControllerUpdate(event);
if (event instanceof ConfigureCompleteEvent)
{
Processor processor = (Processor) event.
getSourceController();
if (processor != null)
{
try
{
processor.setContentDescriptor(contentDescriptor);
}
catch (NotConfiguredError nce)
{
logger.error(
"Failed to set ContentDescriptor to Processor.",
nce);
}
if (format != null)
setProcessorFormat(processor, format);
}
}
}
};
}
/**
* Notifies all currently registered <tt>SimpleAudioLevelListener</tt>s
* that our local media now has audio level <tt>level</tt>.

@ -70,6 +70,13 @@ public class MediaDeviceSession
*/
private boolean captureDeviceIsConnected;
/**
* The <tt>ContentDescriptor</tt> which specifies the content type in which
* this <tt>MediaDeviceSession</tt> is to output the media captured by its
* <tt>MediaDevice</tt>.
*/
private ContentDescriptor contentDescriptor;
/**
* The <tt>MediaDevice</tt> used by this instance to capture and play back
* media.
@ -497,6 +504,27 @@ public void controllerUpdate(ControllerEvent event)
return null;
}
/**
* Creates a <tt>ContentDescriptor</tt> to be set on a specific
* <tt>Processor</tt> of captured media to be sent to the remote peer.
* Allows extenders to override. The default implementation returns
* {@link ContentDescriptor#RAW_RTP}.
*
* @param processor the <tt>Processor</tt> of captured media to be sent to
* the remote peer which is to have its <tt>contentDescriptor</tt> set to
* the returned <tt>ContentDescriptor</tt>
* @return a <tt>ContentDescriptor</tt> to be set on the specified
* <tt>processor</tt> of captured media to be sent to the remote peer
*/
protected ContentDescriptor createProcessorContentDescriptor(
Processor processor)
{
return
(contentDescriptor == null)
? new ContentDescriptor(ContentDescriptor.RAW_RTP)
: contentDescriptor;
}
/**
* Makes sure {@link #captureDevice} is disconnected.
*/
@ -705,13 +733,13 @@ public Format getProcessorFormat()
continue;
Format jmfFormat = trackControl.getFormat();
MediaType type = jmfFormat instanceof VideoFormat
? MediaType.VIDEO : MediaType.AUDIO;
MediaType type
= (jmfFormat instanceof VideoFormat)
? MediaType.VIDEO
: MediaType.AUDIO;
if(mediaType.equals((type)))
{
if(mediaType.equals(type))
return jmfFormat;
}
}
}
return null;
@ -1113,10 +1141,8 @@ protected void processorControllerUpdate(ControllerEvent event)
{
try
{
processor
.setContentDescriptor(
new ContentDescriptor(
ContentDescriptor.RAW_RTP));
processor.setContentDescriptor(
createProcessorContentDescriptor(processor));
}
catch (NotConfiguredError nce)
{
@ -1219,6 +1245,25 @@ protected void receiveStreamChanged(
{
}
/**
* Sets the <tt>ContentDescriptor</tt> which specifies the content type in
* which this <tt>MediaDeviceSession</tt> is to output the media captured by
* its <tt>MediaDevice</tt>. The default content type in which
* <tt>MediaDeviceSession</tt> outputs the media captured by its
* <tt>MediaDevice</tt> is {@link ContentDescriptor#RAW_RTP}.
*
* @param contentDescriptor the <tt>ContentDescriptor</tt> which specifies
* the content type in which this <tt>MediaDeviceSession</tt> is to output
* the media captured by its <tt>MediaDevice</tt>
*/
public void setContentDescriptor(ContentDescriptor contentDescriptor)
{
if (contentDescriptor == null)
throw new NullPointerException("contentDescriptor");
this.contentDescriptor = contentDescriptor;
}
/**
* Sets the <tt>MediaFormat</tt> in which this <tt>MediaDeviceSession</tt>
* outputs the media captured by its <tt>MediaDevice</tt>.

@ -1189,6 +1189,15 @@ protected XMPPConnection getConnection()
return connection;
}
/**
* Determines whether a specific <tt>XMPPException</tt> signals that
* attempted authentication has failed.
*
* @param ex the <tt>XMPPException</tt> which is to be determined whether it
* signals that attempted authentication has failed
* @return <tt>true</tt> if the specified <tt>ex</tt> signals that attempted
* authentication has failed; otherwise, <tt>false</tt>
*/
private boolean isAuthenticationFailed(XMPPException ex)
{
String exMsg = ex.getMessage().toLowerCase();
@ -1196,16 +1205,12 @@ private boolean isAuthenticationFailed(XMPPException ex)
// as there are no types or reasons for XMPPException
// we try determine the reason according to their message
// all messages that were found in smack 3.1.0 were took in count
if(exMsg.indexOf("authentication failed") != -1
|| (exMsg.indexOf("authentication") != -1
&& exMsg.indexOf("failed") != -1)
|| exMsg.indexOf("login failed") != -1
|| exMsg.indexOf("unable to determine password") != -1)
{
return true;
}
else
return false;
return
(exMsg.indexOf("authentication failed") != -1)
|| ((exMsg.indexOf("authentication") != -1)
&& (exMsg.indexOf("failed") != -1))
|| (exMsg.indexOf("login failed") != -1)
|| (exMsg.indexOf("unable to determine password") != -1);
}
/**

@ -3176,7 +3176,7 @@ protected void processSuccessResponse(
/**
* Implements the corresponding <tt>SipListener</tt> method by
* terminating the corresponding subsctiption and polling the related
* terminating the corresponding subscription and polling the related
* contact.
*
* @param requestEvent the event containing the request that was \

@ -301,7 +301,26 @@ public List<String> getKnownEventsList()
return this.registeredEvents;
}
/**
* Overrides
* {@link AbstractProtocolProviderService#fireRegistrationStateChanged(
* RegistrationState, RegistrationState, int, String)} in order to add
* enabling/disabling XCAP functionality in accord with the current
* <tt>RegistrationState</tt> of this
* <tt>ProtocolProviderServiceSipImpl</tt>.
*
* @param oldState the state that the provider had before the change
* occurred
* @param newState the state that the provider is currently in
* @param reasonCode a value corresponding to one of the REASON_XXX fields
* of the <tt>RegistrationStateChangeEvent</tt> class, indicating the reason
* for this state transition
* @param reason a <tt>String</tt> further explaining the reason code or
* <tt>null</tt> if no such explanation is necessary
* @see AbstractProtocolProviderService#fireRegistrationStateChanged(
* RegistrationState, RegistrationState, int, String)
*/
@Override
public void fireRegistrationStateChanged(RegistrationState oldState,
RegistrationState newState,
int reasonCode,

@ -31,8 +31,17 @@ public interface Recorder
* format in which media is to be recorded by <tt>Recorder</tt> (e.g. the
* media being sent and received in a <tt>Call</tt>).
*/
public static final String CALL_FORMAT
= "net.java.sip.communicator.impl.neomedia.CALL_FORMAT";
public static final String FORMAT
= "net.java.sip.communicator.impl.neomedia.Recorder.FORMAT";
/**
* Adds a new <tt>Listener</tt> to the list of listeners interested in
* notifications from this <tt>Recorder</tt>.
*
* @param listener the new <tt>Listener</tt> to be added to the list of
* listeners interested in notifications from this <tt>Recorder</tt>
*/
public void addListener(Listener listener);
/**
* Gets a list of the formats in which this <tt>Recorder</tt> supports
@ -43,11 +52,22 @@ public interface Recorder
*/
public List<String> getSupportedFormats();
/**
* Removes an existing <tt>Listener</tt> from the list of listeners
* interested in notifications from this <tt>Recorder</tt>.
*
* @param listener the existing <tt>Listener</tt> to be removed from the
* list of listeners interested in notifications from this <tt>Recorder</tt>
*/
public void removeListener(Listener listener);
/**
* Starts the recording of the media associated with this <tt>Recorder</tt>
* (e.g. the media being sent and received in a <tt>Call</tt>) into a file
* with a specific name.
*
* @param format the format into which the media associated with this
* <tt>Recorder</tt> is to be recorded into the specified file
* @param filename the name of the file into which the media associated with
* this <tt>Recorder</tt> is to be recorded
* @throws IOException if anything goes wrong with the input and/or output
@ -55,7 +75,7 @@ public interface Recorder
* @throws MediaException if anything else goes wrong while starting the
* recording of media performed by this <tt>Recorder</tt>
*/
public void start(String filename)
public void start(String format, String filename)
throws IOException,
MediaException;
@ -65,4 +85,21 @@ public void start(String filename)
* been started and prepares this <tt>Recorder</tt> for garbage collection.
*/
public void stop();
/**
* Represents a listener interested in notifications from a <tt>Recorder</tt>.
*
* @author Lubomir Marinov
*/
public interface Listener
{
/**
* Notifies this <tt>Listener</tt> that a specific <tt>Recorder</tt> has
* stopped recording the media associated with it.
*
* @param recorder the <tt>Recorder</tt> which has stopped recording its
* associated media
*/
public void recorderStopped(Recorder recorder);
}
}

@ -570,8 +570,40 @@ public void removeVideoPropertyChangeListener(
public Recorder createRecorder()
throws OperationFailedException
{
return
ProtocolMediaActivator.getMediaService().createRecorder(
final Recorder recorder
= ProtocolMediaActivator.getMediaService().createRecorder(
getDefaultDevice(MediaType.AUDIO));
if (recorder != null)
{
// Make sure the recorder is stopped when this call ends.
final CallChangeListener callChangeListener
= new CallChangeAdapter()
{
@Override
public void callStateChanged(CallChangeEvent evt)
{
if (CallState.CALL_ENDED.equals(evt.getNewValue()))
recorder.stop();
}
};
addCallChangeListener(callChangeListener);
/*
* If the recorder gets stopped earlier than this call ends, don't
* wait for the end of the call because callChangeListener will keep
* a reference to the stopped recorder.
*/
recorder.addListener(
new Recorder.Listener()
{
public void recorderStopped(Recorder recorder)
{
removeCallChangeListener(callChangeListener);
}
});
}
return recorder;
}
}

@ -21,8 +21,11 @@
*/
public class NetworkUtils
{
private static final Logger logger
= Logger.getLogger(NetworkUtils.class);
/**
* The <tt>Logger</tt> used by the <tt>NetworkUtils</tt> class for logging
* output.
*/
private static final Logger logger = Logger.getLogger(NetworkUtils.class);
/**
* A string containing the "any" local address for IPv6.
@ -436,7 +439,7 @@ public static InetAddress getInetAddress(String hostAddress)
* The records are ordered against the SRV record priority
* @param domain the name of the domain we'd like to resolve (_proto._tcp
* included).
* @param port
* @param port the port number of the returned <tt>InetSocketAddress</tt>
* @return an array of InetSocketAddress containing records returned by the DNS
* server - address and port .
* @throws ParseException if <tt>domain</tt> is not a valid domain name.
@ -472,7 +475,7 @@ public static InetSocketAddress getARecord(String domain, int port)
* The records are ordered against the SRV record priority
* @param domain the name of the domain we'd like to resolve (_proto._tcp
* included).
* @param port
* @param port the port number of the returned <tt>InetSocketAddress</tt>
* @return an array of InetSocketAddress containing records returned by the DNS
* server - address and port .
* @throws ParseException if <tt>domain</tt> is not a valid domain name.

@ -18,6 +18,12 @@ public class SipCommFileDialogImpl
extends FileDialog
implements SipCommFileChooser
{
/**
* The serialization-related version of the <tt>SipCommFileDialogImpl</tt>
* class explicitly defined to silence a related warning (e.g. in Eclipse
* IDE) since the <tt>SipCommFileDialogImpl</tt> class does not add instance
* fields.
*/
private static final long serialVersionUID = 0L;
/**
@ -67,7 +73,7 @@ public void setStartPath(String path)
if ((file != null) && !file.isDirectory())
{
setDirectory(file.getParent());
setFile(path);
setFile(file.getName());
}
else
setDirectory(path);

Loading…
Cancel
Save