com.skype.Call Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* Copyright (c) 2006-2007 Koji Hisano - UBION Inc. Developer
* Copyright (c) 2006-2007 UBION Inc.
*
* Copyright (c) 2006-2007 Skype Technologies S.A.
*
* Skype4Java is licensed under either the Apache License, Version 2.0 or
* the Eclipse Public License v1.0.
* You may use it freely in commercial and non-commercial products.
* You may obtain a copy of the licenses at
*
* the Apache License - http://www.apache.org/licenses/LICENSE-2.0
* the Eclipse Public License - http://www.eclipse.org/legal/epl-v10.html
*
* If it is possible to cooperate with the publicity of Skype4Java, please add
* links to the Skype4Java web site
* in your web site or documents.
*
* Contributors:
* Koji Hisano - initial API and implementation
* Bart Lamot - good javadocs
* Fabio D. C. Depin - continued implementation API
******************************************************************************/
package com.skype;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.skype.connector.Connector;
import com.skype.connector.ConnectorException;
/**
* This class implements all features of the SKYPE CALL protocol.
* @see Skype API reference - Commands - Making and managing voice calls
* @see Skype API reference - Objects - CALL object
* @author Koji Hisano
*/
public final class Call extends SkypeObject {
/**
* Collection of Call objects, filled at runtime when new CALL objects are created (by events or by application).
*/
private static final Map calls = new HashMap();
/**
* Returns the Call object by the specified id.
*
* @param id whose associated Call object is to be returned.
* @return Call object with ID == id.
*/
static Call getInstance(final String id) {
synchronized(calls) {
if (!calls.containsKey(id)) {
calls.put(id, new Call(id));
}
return calls.get(id);
}
}
/**
* Returns the Call object by the specified id.
*
* @param id whose associated Call object is to be returned.
* @param callListener the listener to add.
* @return Call object with ID == id.
*/
static Call getInstance(final String id, final CallMonitorListener callListener) {
synchronized (calls) {
if (!calls.containsKey(id)) {
calls.put(id, new Call(id, callListener));
}
return calls.get(id);
}
}
/**
* Enumeration of call status types.
*/
public enum Status {
/**
* UNPLACED - call was never placed.
* ROUTING - call is currently being routed.
* EARLYMEDIA - with pstn it is possible that before a call is established, early media is played. For example it can be a calling tone or a waiting message such as all operators are busy.
* FAILED - call failed - try to get a FAILUREREASON for more information.
* RINGING - currently ringing.
* INPROGRESS - call is in progress.
* ONHOLD - call is placed on hold.
* FINISHED - call is finished.
* MISSED - call was missed.
* REFUSED - call was refused.
* BUSY - destination was busy.
* CANCELLED (Protocol 2).
* VM_BUFFERING_GREETING - voicemail greeting is being downloaded.
* VM_PLAYING_GREETING - voicemail greeting is being played.
* VM_RECORDING - voicemail is being recorded.
* VM_UPLOADING - voicemail recording is finished and uploaded into server.
* VM_SENT - voicemail has successfully been sent.
* VM_CANCELLED - leaving voicemail has been cancelled.
* VM_FAILED - leaving voicemail failed; check FAILUREREASON.
* LOCALHOLD - call was placed on hold by local user.
* REMOTEHOLD - call was placed on hold by remote user.
* WAITING_REDIAL_COMMAND - call to PSTN was rejected by remote party.
* REDIAL_PENDING - redial button on the Call Phones tab of the Skype interface is pressed.
*/
UNPLACED, ROUTING, EARLYMEDIA, FAILED, RINGING, INPROGRESS, ONHOLD,
FINISHED, MISSED, REFUSED, BUSY, CANCELLED, VM_BUFFERING_GREETING,
VM_PLAYING_GREETING, VM_RECORDING, VM_UPLOADING, VM_SENT, VM_CANCELLED,
VM_FAILED, TRANSFERRING, TRANSFERRED, LOCALHOLD, REMOTEHOLD,
WAITING_REDIAL_COMMAND, REDIAL_PENDING;
}
/**
* Enumeration of CALL types.
*/
public enum Type {
/**
* INCOMING_PSTN - incoming call from PSTN.
* OUTGOING_PSTN - outgoing call to PSTN.
* INCOMING_P2P - incoming call from P2P.
* OUTGOING_P2P - outgoing call to P2P.
*/
INCOMING_PSTN, OUTGOING_PSTN, INCOMING_P2P, OUTGOING_P2P;
}
/**
* Enumeration of video status types.
*/
public enum VideoStatus {
/**
* NOT_AVAILABLE - The user does not have video capability because video is disabled or a webcam is unplugged).
* AVAILABLE - The user is video-capable but the video is not running (can occur during a manual send).
* STARTING - The video is sending but is not yet running at full speed.
* REJECTED - The receiver rejects the video feed (can occur during a manual receive).
* RUNNING - The video is actively running.
* STOPPING - The active video is in the process of stopping but has not halted yet.
* PAUSED - The video call is placed on hold.
*/
NOT_AVAILABLE, AVAILABLE, STARTING, REJECTED, RUNNING, STOPPING, PAUSED
}
/**
* Enumeration of DTMF types.
*/
public enum DTMF {
TYPE_0, TYPE_1, TYPE_2, TYPE_3, TYPE_4, TYPE_5, TYPE_6, TYPE_7, TYPE_8, TYPE_9, TYPE_SHARP('#'), TYPE_ASTERISK('*');
private final char type;
DTMF() {
type = name().charAt("TYPE_".length());
}
DTMF(char type) {
this.type = type;
}
char getType() {
return type;
}
}
/**
* Enumeration of video enabled status types.
*/
private enum VideoEnabledStatus {
/**
* VIDEO_NONE.
* VIDEO_SEND_ENABLED.
* VIDEO_RECV_ENABLED.
* VIDEO_BOTH_ENABLED.
*/
VIDEO_NONE, VIDEO_SEND_ENABLED, VIDEO_RECV_ENABLED, VIDEO_BOTH_ENABLED;
}
/**
* The CALL objects ID.
*/
private final String id;
/**
* List of listeners to CALL objects.
*/
@Deprecated
private final List listeners = Collections.synchronizedList(new ArrayList());
/**
* List of monitor listeners to CALL objects.
*/
private final List monitorListeners = Collections.synchronizedList(new ArrayList());
/**
* Previous status.
*/
private Status oldStatus;
/**
* Exception handler to CALL object.
*/
private SkypeExceptionHandler exceptionHandler;
/**
* Flag for fired events.
*/
private boolean isCallListenerEventFired;
/**
* Consturctor. Use getInstance instead of constructor.
*
* @param newId the ID of this CALL object.
*/
private Call(final String newId) {
this.id = newId;
}
/**
* Consturctor. Use getInstance instead of constructor.
*
* @param newId the ID of this CALL object.
* @param callListener the monitor listener to add..
*/
private Call(final String newId, final CallMonitorListener callListener) {
this.id = newId;
addCallMonitorListener(callListener);
}
/**
* Use the CALL ID as the hashcode.
* @return id.
*/
@Override
public int hashCode() {
return id.hashCode();
}
/**
* Implement a equals check method.
* Check ID field for equalness.
* @param compared the object to compare to.
* @return true if objects are equal.
*/
@Override
public boolean equals(final Object compared) {
if (compared instanceof Call) {
return id.equals(((Call) compared).id);
}
return false;
}
/**
* Return the ID of the CALL object.
* @return the ID.
*/
public String getId() {
return id;
}
/**
* Add a listener for the monitor field. The listener will be triggered every
* time the status of this CALL object is changed.
*
* @param listener the listener to add.
*/
public void addCallMonitorListener(final CallMonitorListener callListener) {
Utils.checkNotNull("listener", callListener);
monitorListeners.add(callListener);
}
/**
* Remove a listener to the monitor of this CALL object. If listener is
* already removed nothing happens.
*
* @param listener the listener to remove.
*/
public void removeCallMonitorListener(final CallMonitorListener callListener) {
Utils.checkNotNull("listener", callListener);
monitorListeners.remove(callListener);
}
/**
* Trigger all Status listeners because the status of this CALL object has
* changed.
*
* @param status the new status.
*/
void fireCallMonitor(final Status status) {
if (status == oldStatus) {
return;
}
oldStatus = status;
for (CallMonitorListener listener : monitorListeners) {
try {
listener.callMonitor(this, status);
} catch (Throwable e) {
Utils.handleUncaughtException(e, exceptionHandler);
}
}
}
/**
* Add a listener for the Status field.
* The listener will be triggered every time the status of this CALL object is changed.
* @param listener the listener to add.
*/
@Deprecated
public void addCallStatusChangedListener(final CallStatusChangedListener listener) {
Utils.checkNotNull("listener", listener);
listeners.add(listener);
}
/**
* Remove a listener to the status of this CALL object.
* If listener is already removed nothing happens.
* @param listener the listener to remove.
*/
@Deprecated
public void removeCallStatusChangedListener(final CallStatusChangedListener listener) {
Utils.checkNotNull("listener", listener);
listeners.remove(listener);
}
/**
* Trigger all Status listeners because the status of this CALL object has changed.
* @param status the new status.
*/
@Deprecated
void fireStatusChanged(final Status status) {
CallStatusChangedListener[] callListeners = this.listeners.toArray(new CallStatusChangedListener[0]);
if (status == oldStatus) {
return;
}
oldStatus = status;
for (CallStatusChangedListener listener : callListeners) {
try {
listener.statusChanged(status);
} catch (Throwable e) {
Utils.handleUncaughtException(e, exceptionHandler);
}
}
}
/**
* Put this CALL on hold.
* @throws SkypeException when connection is bad.
*/
public void hold() throws SkypeException {
setStatus("ONHOLD");
}
/**
* Resume an on hold CALL.
* @throws SkypeException when connection is bad.
*/
public void resume() throws SkypeException {
setStatus("INPROGRESS");
}
/**
* End a CALL.
* @throws SkypeException when connection is bad.
*/
public void finish() throws SkypeException {
setStatus("FINISHED");
}
/**
* Answer a ringing CALL.
* @throws SkypeException when connection is bad.
*/
public void answer() throws SkypeException {
setStatus("INPROGRESS");
}
/**
* Cancel a CALL.
* @throws SkypeException when connection is bad.
*/
public void cancel() throws SkypeException {
setStatus("FINISHED");
}
/**
* Change the status of this CALL object.
* @param status The new status to set.
* @throws SkypeException when connection is bad.
*/
private void setStatus(final String status) throws SkypeException {
try {
String response = Connector.getInstance().executeWithId("SET CALL " + getId() + " STATUS " + status, "CALL " + getId() + " STATUS ");
Utils.checkError(response);
} catch (ConnectorException e) {
Utils.convertToSkypeException(e);
}
}
/**
* Forward a ringing CALL to profile forwarding rule.
* @throws SkypeException when connection is bad.
*/
public void forward() throws SkypeException {
Utils.executeWithErrorCheck("ALTER CALL " + getId() + " END FORWARD_CALL");
}
/**
* Redirect a ringing CALL to a voice mail.
* @throws SkypeException when connection is bad.
*/
public void redirectToVoiceMail() throws SkypeException {
Utils.executeWithErrorCheck("ALTER CALL " + getId() + " END FORWARD_CALL");
}
/**
* Send a DTMF command.
* @throws SkypeException when connection is bad.
*/
public void send(DTMF command) throws SkypeException {
Utils.executeWithErrorCheck("SET CALL " + getId() + " DTMF " + command.getType());
}
/**
* Get the starttime of this CALL object.
* @return the starttime.
* @throws SkypeException when connection is bad.
*/
public Date getStartTime() throws SkypeException {
return Utils.parseUnixTime(getProperty("TIMESTAMP"));
}
/**
* Return the Skype user who is the partner in this CALL.
* @return the other Skype user.
* @throws SkypeException when connection is bad.
*/
public User getPartner() throws SkypeException {
return User.getInstance(getPartnerId());
}
/**
* Return the Skype handle of the other user in this CALL.
* @return The handle.
* @throws SkypeException when connection is bad.
*/
public String getPartnerId() throws SkypeException {
return getProperty("PARTNER_HANDLE");
}
/**
* Return the DISPLAYNAME of the other user in this CALL.
* @return DISPLAYNAME.
* @throws SkypeException when connection is bad.
*/
public String getPartnerDisplayName() throws SkypeException {
return getProperty("PARTNER_DISPNAME");
}
/**
* Return the type of this call.
* @return call type.
* @throws SkypeException when connection is bad.
*/
public Type getType() throws SkypeException {
return Type.valueOf(getProperty("TYPE"));
}
/**
* Return the current status of this CALL.
* @return Status of this call.
* @throws SkypeException when connection is bad.
*/
public Status getStatus() throws SkypeException {
// call Utils#getPropertyWithCommandId(String, String, String) to prevent new event notification
return Status.valueOf(Utils.getPropertyWithCommandId("CALL", getId(), "STATUS"));
}
/**
* Return the duration of this CALL.
* @return duration of this call.
* @throws SkypeException when connection is bad.
*/
public int getDuration() throws SkypeException {
return Integer.parseInt(getProperty("DURATION"));
}
/**
* Return the reason of failure.
* @return FAILUREREASON.
* @throws SkypeException when connection is bad.
*/
public int getErrorCode() throws SkypeException {
return Integer.parseInt(getProperty("FAILUREREASON"));
}
/**
* Start or stop receiving video on this call.
* @param videoStatus enable = true.
* @throws SkypeException when connection is bad.
*/
public void setReceiveVideoEnabled(final boolean videoStatus) throws SkypeException {
String value = videoStatus ? "START_VIDEO_SEND" : "STOP_VIDEO_SEND";
try {
String response = Connector.getInstance().execute("ALTER CALL " + getId() + " " + value);
Utils.checkError(response);
} catch (ConnectorException e) {
Utils.convertToSkypeException(e);
}
}
/**
* Check if video receiving is enabled for this CALL.
* @return true if enabled.
* @throws SkypeException when connection is bad.
*/
public boolean isReceiveVideoEnabled() throws SkypeException {
VideoEnabledStatus enabled = VideoEnabledStatus.valueOf(getProperty("VIDEO_STATUS"));
switch (enabled) {
case VIDEO_NONE:
case VIDEO_SEND_ENABLED:
return false;
case VIDEO_RECV_ENABLED:
case VIDEO_BOTH_ENABLED:
return true;
default:
return false;
}
}
/**
* Start or stop sending video with this call.
* @param videoStatus enable = true.
* @throws SkypeException when connection is bad.
*/
public void setSendVideoEnabled(final boolean videoStatus) throws SkypeException {
String value = videoStatus ? "START_VIDEO_RECEIVE" : "STOP_VIDEO_RECEIVE";
try {
String response = Connector.getInstance().execute("ALTER CALL " + getId() + " " + value);
Utils.checkError(response);
} catch (ConnectorException e) {
Utils.convertToSkypeException(e);
}
}
/**
* Check if video sending is enabled for this CALL.
* @return true if enabled.
* @throws SkypeException when connection is bad.
*/
public boolean isSendVideoEnabled() throws SkypeException {
VideoEnabledStatus enabled = VideoEnabledStatus.valueOf(getProperty("VIDEO_STATUS"));
switch (enabled) {
case VIDEO_NONE:
case VIDEO_RECV_ENABLED:
return false;
case VIDEO_SEND_ENABLED:
case VIDEO_BOTH_ENABLED:
return true;
default:
return false;
}
}
/**
* Return the status of receiving video with this CALL.
* @return videoStatus of this call.
* @throws SkypeException when connection is bad.
*/
public VideoStatus getReceiveVideoStatus() throws SkypeException {
return VideoStatus.valueOf(getProperty("VIDEO_RECEIVE_STATUS"));
}
/**
* Return the status of sending video with this CALL.
* @return videoStatus of this call.
* @throws SkypeException when connection is bad.
*/
public VideoStatus getSendVideoStatus() throws SkypeException {
return VideoStatus.valueOf(getProperty("VIDEO_SEND_STATUS"));
}
/**
* Return the conference ID of this CALL.
* @return The conference ID
* @throws SkypeException when connection is bad.
*/
public String getConferenceId() throws SkypeException {
return getProperty("CONF_ID");
}
public String getParticipantsCount() throws SkypeException {
return getProperty("CONF_PARTICIPANTS_COUNT");
}
/**
* Return property of this CALL.
* @param name property name.
* @return The value of the property.
* @throws SkypeException when connection is bad.
*/
private String getProperty(final String name) throws SkypeException {
return Utils.getProperty("CALL", getId(), name);
}
/**
* Check if an event is fired.
* @return true if fired.
*/
boolean isCallListenerEventFired() {
return isCallListenerEventFired;
}
/**
* Set the eventfired flag.
* @param fired the value of the flag.
*/
void setCallListenerEventFired(final boolean fired) {
isCallListenerEventFired = fired;
}
public void setFileInput(File file) throws SkypeException {
if (file == null) {
clearFileInput();
return;
}
Map inputStreams = getInputStreams();
inputStreams.put("FILE", file.getAbsolutePath());
setInputStreams(inputStreams);
}
public void clearFileInput() throws SkypeException {
Map inputStreams = getInputStreams();
inputStreams.remove("FILE");
setInputStreams(inputStreams);
}
public void setFileOutput(File file) throws SkypeException {
if (file == null) {
clearFileOutput();
return;
}
Map outputStreams = getOutputStreams();
outputStreams.put("FILE", file.getAbsolutePath());
setOutputStreams(outputStreams);
}
public void clearFileOutput() throws SkypeException {
Map outputStreams = getOutputStreams();
outputStreams.remove("FILE");
setOutputStreams(outputStreams);
}
public void setFileCaptureMic(File file) throws SkypeException {
if (file == null) {
clearFileCaptureMic();
return;
}
Map captureMicStreams = getCaptureMicStreams();
captureMicStreams.put("FILE", file.getAbsolutePath());
setCaptureMicStreams(captureMicStreams);
}
public void clearFileCaptureMic() throws SkypeException {
Map inputStreams = getCaptureMicStreams();
inputStreams.remove("FILE");
setCaptureMicStreams(inputStreams);
}
public void setPortInput(int port) throws SkypeException {
if (port <= 0) {
clearPortInput();
return;
}
Map inputStreams = getInputStreams();
inputStreams.put("PORT", "" + port);
setInputStreams(inputStreams);
}
public void clearPortInput() throws SkypeException {
Map inputStreams = getInputStreams();
inputStreams.remove("PORT");
setInputStreams(inputStreams);
}
public void setPortOutput(int port) throws SkypeException {
if (port <= 0) {
clearPortOutput();
return;
}
Map outputStreams = getOutputStreams();
outputStreams.put("PORT", "" + port);
setOutputStreams(outputStreams);
}
public void clearPortOutput() throws SkypeException {
Map outputStreams = getOutputStreams();
outputStreams.remove("PORT");
setOutputStreams(outputStreams);
}
public void setPortCaptureMic(int port) throws SkypeException {
if (port <= 0) {
clearPortCaptureMic();
return;
}
Map captureMicStreams = getCaptureMicStreams();
captureMicStreams.put("PORT", "" + port);
setCaptureMicStreams(captureMicStreams);
}
public void clearPortCaptureMic() throws SkypeException {
Map captureMicStreams = getCaptureMicStreams();
captureMicStreams.remove("PORT");
setCaptureMicStreams(captureMicStreams);
}
private Map getInputStreams() throws SkypeException {
return getStreams("INPUT");
}
private Map getOutputStreams() throws SkypeException {
return getStreams("OUTPUT");
}
private Map getCaptureMicStreams() throws SkypeException {
return getStreams("CAPTURE_MIC");
}
private Map getStreams(String type) throws SkypeException {
String response = Utils.getProperty("CALL", getId(), type);
Utils.checkError(response);
if("".equals(response)) {
return new HashMap();
}
Map r = new HashMap();
for(String element: response.split(", ")) {
int index = element.indexOf('=');
String key = element.substring(0, index);
String value = element.substring(index + 2, element.length() - 1); // remove first and last double quotation mark
r.put(key, value);
}
return r;
}
private void setInputStreams(Map inputStreams) throws SkypeException {
setStreams("INPUT", inputStreams);
}
private void setOutputStreams(Map outputStreams) throws SkypeException {
setStreams("OUTPUT", outputStreams);
}
private void setCaptureMicStreams(Map captureMicStreams) throws SkypeException {
setStreams("CAPTURE_MIC", captureMicStreams);
}
private void setStreams(String type, Map streams) throws SkypeException {
try {
StringBuilder parameters = new StringBuilder();
for(String key: streams.keySet()) {
parameters.append(" ");
parameters.append(key);
parameters.append("=\"");
parameters.append(streams.get(key));
parameters.append("\"");
}
String response = Connector.getInstance().execute("ALTER CALL " + getId() + " SET_" + type + parameters);
Utils.checkError(response);
} catch(ConnectorException e) {
Utils.convertToSkypeException(e);
}
}
public boolean canTransferTo(String skypeId) throws SkypeException {
try {
String command = "GET CALL " + getId() + " CAN_TRANSFER " + skypeId;
String responseHeader = "CALL " + getId() + " CAN_TRANSFER " + skypeId + " ";
String response = Connector.getInstance().execute(command, responseHeader);
Utils.checkError(response);
return Boolean.parseBoolean(response.substring((responseHeader).length()));
} catch (ConnectorException e) {
Utils.convertToSkypeException(e);
return false;
}
}
public void transferTo(String... skypeIds) throws SkypeException {
Utils.checkNotNull("skypeIds", skypeIds);
try {
String response = Connector.getInstance().execute("ALTER CALL " + getId() + " TRANSFER \"" + Utils.convertToCommaSeparatedString(skypeIds) + "\"");
Utils.checkError(response);
} catch (ConnectorException e) {
Utils.convertToSkypeException(e);
}
}
}