aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build.xml22
-rw-r--r--lib/oscar.client.run.properties3
-rw-r--r--lib/oscar.unit.test.properties6
-rw-r--r--lib/testing.properties1
-rwxr-xr-xsrc/net/java/sip/communicator/impl/callhistory/CallHistoryActivator.java69
-rw-r--r--src/net/java/sip/communicator/impl/callhistory/CallHistoryServiceImpl.java900
-rw-r--r--src/net/java/sip/communicator/impl/callhistory/callhistory.manifest.mf19
-rwxr-xr-xsrc/net/java/sip/communicator/impl/callhistory/callhistory.metadata.xml29
-rw-r--r--src/net/java/sip/communicator/impl/protocol/mock/MockCall.java161
-rw-r--r--src/net/java/sip/communicator/impl/protocol/mock/MockCallParticipant.java160
-rw-r--r--src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java284
-rw-r--r--src/net/java/sip/communicator/service/callhistory/CallHistoryService.java58
-rw-r--r--src/net/java/sip/communicator/service/callhistory/CallParticipantRecord.java88
-rw-r--r--src/net/java/sip/communicator/service/callhistory/CallRecord.java163
-rw-r--r--src/net/java/sip/communicator/service/callhistory/event/CallHistorySearchProgressListener.java40
-rw-r--r--src/net/java/sip/communicator/service/callhistory/event/ProgressEvent.java74
-rw-r--r--test/net/java/sip/communicator/slick/callhistory/CallHistoryServiceLick.java56
-rw-r--r--test/net/java/sip/communicator/slick/callhistory/TestCallHistoryService.java353
-rwxr-xr-xtest/net/java/sip/communicator/slick/callhistory/callhistory.slick.manifest.mf25
19 files changed, 2508 insertions, 3 deletions
diff --git a/build.xml b/build.xml
index 078b3c0..10c6e3e 100644
--- a/build.xml
+++ b/build.xml
@@ -560,6 +560,7 @@
<target name="bundles"
depends="bundle-util,bundle-configuration,bundle-configuration-slick,
bundle-history,bundle-history-slick,bundle-messagehistory, bundle-msghistory-slick,
+ bundle-callhistory, bundle-callhistory-slick,
bundle-netaddr,bundle-netaddr-slick,bundle-slickless,
bundle-slick-runner,bundle-sip,bundle-sip-slick,bundle-fileaccess,
bundle-fileaccess-slick,bundle-media,bundle-media-slick,
@@ -610,6 +611,27 @@
</jar>
</target>
+ <!--BUNDLE-CALLHISTORY-->
+ <target name="bundle-callhistory">
+ <jar compress="false" destfile="${bundles.dest}/callhistory.jar"
+ manifest="src/net/java/sip/communicator/impl/callhistory/callhistory.manifest.mf">
+
+ <zipfileset dir="${dest}/net/java/sip/communicator/service/callhistory"
+ prefix="net/java/sip/communicator/service/callhistory"/>
+ <zipfileset dir="${dest}/net/java/sip/communicator/impl/callhistory"
+ prefix="net/java/sip/communicator/impl/callhistory" />
+ </jar>
+ </target>
+
+ <!--BUNDLE-CALLEHISTORY-SLICK-->
+ <target name="bundle-callhistory-slick">
+ <jar compress="false" destfile="${bundles.dest}/callhistory-slick.jar"
+ manifest="test/net/java/sip/communicator/slick/callhistory/callhistory.slick.manifest.mf">
+ <zipfileset dir="${dest}/net/java/sip/communicator/slick/callhistory"
+ prefix="net/java/sip/communicator/slick/callhistory"/>
+ </jar>
+ </target>
+
<!--BUNDLE-FILEACCESS-->
<target name="bundle-fileaccess">
<jar compress="false" destfile="${bundles.dest}/fileaccess.jar"
diff --git a/lib/oscar.client.run.properties b/lib/oscar.client.run.properties
index e5239d2..80901f9 100644
--- a/lib/oscar.client.run.properties
+++ b/lib/oscar.client.run.properties
@@ -54,7 +54,8 @@ oscar.auto.start.3= \
oscar.auto.start.4= \
file:sc-bundles/history.jar \
- file:sc-bundles/msghistory.jar
+ file:sc-bundles/msghistory.jar \
+ file:sc-bundles/callhistory.jar
oscar.auto.start.66= \
diff --git a/lib/oscar.unit.test.properties b/lib/oscar.unit.test.properties
index b13d50f..4190a4a 100644
--- a/lib/oscar.unit.test.properties
+++ b/lib/oscar.unit.test.properties
@@ -50,7 +50,8 @@ oscar.auto.start.4= \
file:sc-bundles/protocol-sip.jar \
file:sc-bundles/media.jar \
file:sc-bundles/meta-cl.jar \
- file:sc-bundles/msghistory.jar
+ file:sc-bundles/msghistory.jar \
+ file:sc-bundles/callhistory.jar
oscar.auto.start.5= \
file:sc-bundles/slickless.jar \
@@ -65,7 +66,8 @@ oscar.auto.start.5= \
file:sc-bundles/protocol-icq-slick.jar \
file:sc-bundles/protocol-sip-slick.jar \
file:sc-bundles/protocol-jabber-slick.jar \
- file:sc-bundles/msghistory-slick.jar
+ file:sc-bundles/msghistory-slick.jar \
+ file:sc-bundles/callhistory-slick.jar
oscar.auto.start.100= \
file:sc-bundles/slick-runner.jar
diff --git a/lib/testing.properties b/lib/testing.properties
index 9bcb44c..2ea5153 100644
--- a/lib/testing.properties
+++ b/lib/testing.properties
@@ -12,6 +12,7 @@ test.list=ConfigurationServiceLick \
IcqProtocolProviderSlick \
SipProtocolProviderServiceLick \
MsgHistoryServiceLick \
+ CallHistoryServiceLick \
JabberProtocolProviderSlick
# Set the name of the meta contact list file to use during testing so that
diff --git a/src/net/java/sip/communicator/impl/callhistory/CallHistoryActivator.java b/src/net/java/sip/communicator/impl/callhistory/CallHistoryActivator.java
new file mode 100755
index 0000000..2ea38b0
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/callhistory/CallHistoryActivator.java
@@ -0,0 +1,69 @@
+/*
+ * 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.callhistory;
+
+import org.osgi.framework.*;
+import net.java.sip.communicator.service.history.*;
+import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.service.callhistory.*;
+
+/**
+ * Activates the CallHistoryService
+ *
+ * @author Damian Minkov
+ */
+public class CallHistoryActivator
+ implements BundleActivator
+{
+ private static Logger logger =
+ Logger.getLogger(CallHistoryActivator.class);
+
+ private CallHistoryServiceImpl callHistoryService = null;
+
+ /**
+ * Initialize and start call history
+ *
+ * @param bundleContext BundleContext
+ * @throws Exception
+ */
+ public void start(BundleContext bundleContext) throws Exception
+ {
+ try{
+
+ logger.logEntry();
+
+ ServiceReference refHistory = bundleContext.getServiceReference(
+ HistoryService.class.getName());
+
+ HistoryService historyService = (HistoryService)
+ bundleContext.getService(refHistory);
+
+ //Create and start the call history service.
+ callHistoryService =
+ new CallHistoryServiceImpl();
+ // set the configuration and history service
+ callHistoryService.setHistoryService(historyService);
+
+ callHistoryService.start(bundleContext);
+
+ bundleContext.registerService(
+ CallHistoryService.class.getName(), callHistoryService, null);
+
+ logger.info("Call History Service ...[REGISTERED]");
+ }
+ finally
+ {
+ logger.logExit();
+ }
+
+ }
+
+ public void stop(BundleContext bundleContext) throws Exception
+ {
+
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/callhistory/CallHistoryServiceImpl.java b/src/net/java/sip/communicator/impl/callhistory/CallHistoryServiceImpl.java
new file mode 100644
index 0000000..c360d4a
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/callhistory/CallHistoryServiceImpl.java
@@ -0,0 +1,900 @@
+/*
+ * 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.callhistory;
+
+import java.io.*;
+import java.util.*;
+
+import org.osgi.framework.*;
+import net.java.sip.communicator.service.callhistory.*;
+import net.java.sip.communicator.service.callhistory.event.*;
+import net.java.sip.communicator.service.contactlist.*;
+import net.java.sip.communicator.service.history.*;
+import net.java.sip.communicator.service.history.event.*;
+import net.java.sip.communicator.service.history.event.ProgressEvent;
+import net.java.sip.communicator.service.history.records.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * The Call History Service stores info about the calls made.
+ * Logs calls info for all protocol providers that support basic telephony
+ * (i.e. those that implement OperationSetBasicTelephony).
+ *
+ * @author Damian Minkov
+ */
+public class CallHistoryServiceImpl
+ implements CallHistoryService,
+ CallListener,
+ ServiceListener
+{
+ /**
+ * The logger for this class.
+ */
+ private static Logger logger = Logger
+ .getLogger(CallHistoryServiceImpl.class);
+
+ private static String[] STRUCTURE_NAMES =
+ new String[] { "callStart", "callEnd", "dir", "callParticipantIDs",
+ "callParticipantStart", "callParticipantEnd" };
+
+ private static HistoryRecordStructure recordStructure =
+ new HistoryRecordStructure(STRUCTURE_NAMES);
+
+ private static final String DELIM = ",";
+
+ /**
+ * The BundleContext that we got from the OSGI bus.
+ */
+ private BundleContext bundleContext = null;
+
+ private HistoryService historyService = null;
+
+ private Object syncRoot_HistoryService = new Object();
+
+ private Hashtable progressListeners = new Hashtable();
+
+ private Vector currentCallRecords = new Vector();
+
+ private HistoryCallChangeListener historyCallChangeListener
+ = new HistoryCallChangeListener();
+
+ public HistoryService getHistoryService()
+ {
+ return historyService;
+ }
+
+ /**
+ * Returns all the calls made by all the contacts
+ * in the supplied metacontact after the given date
+ *
+ * @param contact MetaContact
+ * @param startDate Date the start date of the calls
+ * @return Collection of CallRecords with CallParticipantRecord
+ * @throws RuntimeException
+ */
+ public Collection findByStartDate(MetaContact contact, Date startDate)
+ throws RuntimeException
+ {
+ throw new UnsupportedOperationException("Not implemented yet!");
+ }
+
+ /**
+ * Returns all the calls made after the given date
+ *
+ * @param startDate Date the start date of the calls
+ * @return Collection of CallRecords with CallParticipantRecord
+ * @throws RuntimeException
+ */
+ public Collection findByStartDate(Date startDate) throws RuntimeException
+ {
+ TreeSet result = new TreeSet(new CallRecordComparator());
+ try
+ {
+ // the default ones
+ History history = this.getHistory(null, null);
+ HistoryReader reader = history.getReader();
+ addHistorySearchProgressListeners(reader, 1);
+ QueryResultSet rs = reader.findByStartDate(startDate);
+ while (rs.hasNext())
+ {
+ HistoryRecord hr = (HistoryRecord) rs.next();
+ result.add(convertHistoryRecordToCallRecord(hr));
+ }
+ removeHistorySearchProgressListeners(reader);
+ }
+ catch (IOException ex)
+ {
+ logger.error("Could not read history", ex);
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns all the calls made by all the contacts
+ * in the supplied metacontact before the given date
+ *
+ * @param contact MetaContact
+ * @param endDate Date the end date of the calls
+ * @return Collection of CallRecords with CallParticipantRecord
+ * @throws RuntimeException
+ */
+ public Collection findByEndDate(MetaContact contact, Date endDate)
+ throws RuntimeException
+ {
+ throw new UnsupportedOperationException("Not implemented yet!");
+ }
+
+ /**
+ * Returns all the calls made before the given date
+ *
+ * @param endDate Date the end date of the calls
+ * @return Collection of CallRecords with CallParticipantRecord
+ * @throws RuntimeException
+ */
+ public Collection findByEndDate(Date endDate) throws RuntimeException
+ {
+ TreeSet result = new TreeSet(new CallRecordComparator());
+ try
+ {
+ // the default ones
+ History history = this.getHistory(null, null);
+ HistoryReader reader = history.getReader();
+ addHistorySearchProgressListeners(reader, 1);
+ QueryResultSet rs = reader.findByEndDate(endDate);
+ while (rs.hasNext())
+ {
+ HistoryRecord hr = (HistoryRecord) rs.next();
+ result.add(convertHistoryRecordToCallRecord(hr));
+ }
+ removeHistorySearchProgressListeners(reader);
+ }
+ catch (IOException ex)
+ {
+ logger.error("Could not read history", ex);
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns all the calls made by all the contacts
+ * in the supplied metacontact between the given dates
+ *
+ * @param contact MetaContact
+ * @param startDate Date the start date of the calls
+ * @param endDate Date the end date of the conversations
+ * @return Collection of CallRecords with CallParticipantRecord
+ * @throws RuntimeException
+ */
+ public Collection findByPeriod(MetaContact contact, Date startDate, Date endDate)
+ throws RuntimeException
+ {
+ throw new UnsupportedOperationException("Not implemented yet!");
+ }
+
+ /**
+ * Returns all the calls made between the given dates
+ *
+ * @param startDate Date the start date of the calls
+ * @param endDate Date the end date of the conversations
+ * @return Collection of CallRecords with CallParticipantRecord
+ * @throws RuntimeException
+ */
+ public Collection findByPeriod(Date startDate, Date endDate) throws
+ RuntimeException
+ {
+ TreeSet result = new TreeSet(new CallRecordComparator());
+ try
+ {
+ // the default ones
+ History history = this.getHistory(null, null);
+ HistoryReader reader = history.getReader();
+ addHistorySearchProgressListeners(reader, 1);
+ QueryResultSet rs = reader.findByPeriod(startDate, endDate);
+ while (rs.hasNext())
+ {
+ HistoryRecord hr = (HistoryRecord) rs.next();
+ result.add(convertHistoryRecordToCallRecord(hr));
+ }
+ removeHistorySearchProgressListeners(reader);
+ }
+ catch (IOException ex)
+ {
+ logger.error("Could not read history", ex);
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns the supplied number of calls by all the contacts
+ * in the supplied metacontact
+ *
+ * @param contact MetaContact
+ * @param count calls count
+ * @return Collection of CallRecords with CallParticipantRecord
+ * @throws RuntimeException
+ */
+ public Collection findLast(MetaContact contact, int count)
+ throws RuntimeException
+ {
+ throw new UnsupportedOperationException("Not implemented yet!");
+ }
+
+ /**
+ * Returns the supplied number of calls made
+ *
+ * @param count calls count
+ * @return Collection of CallRecords with CallParticipantRecord
+ * @throws RuntimeException
+ */
+ public Collection findLast(int count) throws RuntimeException
+ {
+ TreeSet result = new TreeSet(new CallRecordComparator());
+ try
+ {
+ // the default ones
+ History history = this.getHistory(null, null);
+ QueryResultSet rs = history.getReader().findLast(count);
+ while (rs.hasNext())
+ {
+ HistoryRecord hr = (HistoryRecord) rs.next();
+ result.add(convertHistoryRecordToCallRecord(hr));
+ }
+ }
+ catch (IOException ex)
+ {
+ logger.error("Could not read history", ex);
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns the history by specified local and remote contact
+ * if one of them is null the default is used
+ *
+ * @param localContact Contact
+ * @param remoteContact Contact
+ * @return History
+ * @throws IOException
+ */
+ private History getHistory(Contact localContact, Contact remoteContact)
+ throws IOException {
+ History retVal = null;
+
+ String localId = localContact == null ? "default" : localContact
+ .getAddress();
+ String remoteId = remoteContact == null ? "default" : remoteContact
+ .getAddress();
+
+ HistoryID historyId = HistoryID.createFromID(new String[] { "callhistory",
+ localId, remoteId });
+
+ if (this.historyService.isHistoryExisting(historyId))
+ {
+ retVal = this.historyService.getHistory(historyId);
+ } else {
+ retVal = this.historyService.createHistory(historyId,
+ recordStructure);
+ }
+
+ return retVal;
+ }
+
+ /**
+ * Used to convert HistoryRecord in CallReord and CallParticipantRecord
+ * which are returned by the finder methods
+ *
+ * @param hr HistoryRecord
+ * @return Object CallRecord
+ */
+ private Object convertHistoryRecordToCallRecord(HistoryRecord hr)
+ {
+ CallRecord result = new CallRecord();
+
+ LinkedList callParticipantIDs = null;
+ LinkedList callParticipantStart = null;
+ LinkedList callParticipantEnd = null;
+
+ // History structure
+ // 0 - callStart
+ // 1 - callEnd
+ // 2 - dir
+ // 3 - callParticipantIDs
+ // 4 - callParticipantStart
+ // 5 - callParticipantEnd
+
+ for (int i = 0; i < hr.getPropertyNames().length; i++)
+ {
+ String propName = hr.getPropertyNames()[i];
+ String value = hr.getPropertyValues()[i];
+
+ if(propName.equals(STRUCTURE_NAMES[0]))
+ result.setStartTime(new Date(Long.parseLong(value)));
+ else if(propName.equals(STRUCTURE_NAMES[1]))
+ result.setEndTime(new Date(Long.parseLong(value)));
+ else if(propName.equals(STRUCTURE_NAMES[2]))
+ result.setDirection(value);
+ else if(propName.equals(STRUCTURE_NAMES[3]))
+ callParticipantIDs = getCSVs(value);
+ else if(propName.equals(STRUCTURE_NAMES[4]))
+ callParticipantStart = getCSVs(value);
+ else if(propName.equals(STRUCTURE_NAMES[5]))
+ callParticipantEnd = getCSVs(value);
+ }
+
+ for (int i = 0; i < callParticipantIDs.size(); i++)
+ {
+
+ CallParticipantRecord cpr = new CallParticipantRecord(
+ (String)callParticipantIDs.get(i),
+ new Date(Long.parseLong((String)callParticipantStart.get(i))),
+ new Date(Long.parseLong((String)callParticipantEnd.get(i)))
+ );
+ result.getParticipantRecords().add(cpr);
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns list of String items contained in the supplied string
+ * separated by DELIM
+ * @param str String
+ * @return LinkedList
+ */
+ private LinkedList getCSVs(String str)
+ {
+ LinkedList result = new LinkedList();
+ StringTokenizer toks = new StringTokenizer(str, DELIM);
+ while(toks.hasMoreTokens())
+ {
+ result.add(toks.nextToken());
+ }
+ return result;
+ }
+
+ /**
+ * starts the service. Check the current registerd protocol providers
+ * which supports BasicTelephony and adds calls listener to them
+ *
+ * @param bc BundleContext
+ */
+ public void start(BundleContext bc)
+ {
+ logger.debug("Starting the call history implementation.");
+ this.bundleContext = bc;
+
+ // start listening for newly register or removed protocol providers
+ bc.addServiceListener(this);
+
+ ServiceReference[] protocolProviderRefs = null;
+ try
+ {
+ protocolProviderRefs = bc.getServiceReferences(
+ ProtocolProviderService.class.getName(),
+ null);
+ }
+ catch (InvalidSyntaxException ex)
+ {
+ // this shouldn't happen since we're providing no parameter string
+ // but let's log just in case.
+ logger.error(
+ "Error while retrieving service refs", ex);
+ return;
+ }
+
+ // in case we found any
+ if (protocolProviderRefs != null)
+ {
+ logger.debug("Found "
+ + protocolProviderRefs.length
+ + " already installed providers.");
+ for (int i = 0; i < protocolProviderRefs.length; i++)
+ {
+ ProtocolProviderService provider = (ProtocolProviderService) bc
+ .getService(protocolProviderRefs[i]);
+
+ this.handleProviderAdded(provider);
+ }
+ }
+ }
+
+ /**
+ * Writes the given record to the history service
+ * @param callRecord CallRecord
+ * @param source Contact
+ * @param destination Contact
+ */
+ private void writeCall(CallRecord callRecord, Contact source,
+ Contact destination)
+ {
+ try {
+ History history = this.getHistory(source, destination);
+ HistoryWriter historyWriter = history.getWriter();
+
+ StringBuffer callParticipantIDs = new StringBuffer();
+ StringBuffer callParticipantStartTime = new StringBuffer();
+ StringBuffer callParticipantEndTime = new StringBuffer();
+
+ Iterator iter = callRecord.getParticipantRecords().iterator();
+ while (iter.hasNext())
+ {
+ if(callParticipantIDs.length() > 0)
+ {
+ callParticipantIDs.append(DELIM);
+ callParticipantStartTime.append(DELIM);
+ callParticipantEndTime.append(DELIM);
+ }
+
+ CallParticipantRecord item = (CallParticipantRecord) iter.next();
+ callParticipantIDs.append(item.getParticipantAddress());
+ callParticipantStartTime.append(String.valueOf(item.getStartTime().getTime()));
+ callParticipantEndTime.append(String.valueOf(item.getEndTime().getTime()));
+ }
+
+ historyWriter.addRecord(new String[] {
+ String.valueOf(callRecord.getStartTime().getTime()),
+ String.valueOf(callRecord.getEndTime().getTime()),
+ callRecord.getDirection(),
+ callParticipantIDs.toString(),
+ callParticipantStartTime.toString(),
+ callParticipantEndTime.toString()},
+ new Date()); // this date is when the history record is written
+ } catch (IOException e)
+ {
+ logger.error("Could not add call to history", e);
+ }
+ }
+
+ /**
+ * Set the configuration service.
+ *
+ * @param historyService HistoryService
+ * @throws IOException
+ * @throws IllegalArgumentException
+ */
+ public void setHistoryService(HistoryService historyService)
+ throws IllegalArgumentException, IOException {
+ synchronized (this.syncRoot_HistoryService)
+ {
+ this.historyService = historyService;
+
+ logger.debug("New history service registered.");
+ }
+ }
+
+ /**
+ * Remove a configuration service.
+ *
+ * @param historyService HistoryService
+ */
+ public void unsetHistoryService(HistoryService historyService)
+ {
+ synchronized (this.syncRoot_HistoryService)
+ {
+ if (this.historyService == historyService)
+ {
+ this.historyService = null;
+
+ logger.debug("History service unregistered.");
+ }
+ }
+ }
+
+ /**
+ * When new protocol provider is registered we check
+ * does it supports BasicTelephony and if so add a listener to it
+ *
+ * @param serviceEvent ServiceEvent
+ */
+ public void serviceChanged(ServiceEvent serviceEvent)
+ {
+ Object sService = bundleContext.getService(serviceEvent.getServiceReference());
+
+ logger.trace("Received a service event for: " + sService.getClass().getName());
+
+ // we don't care if the source service is not a protocol provider
+ if (! (sService instanceof ProtocolProviderService))
+ {
+ return;
+ }
+
+ logger.debug("Service is a protocol provider.");
+ if (serviceEvent.getType() == ServiceEvent.REGISTERED)
+ {
+ logger.debug("Handling registration of a new Protocol Provider.");
+
+ this.handleProviderAdded((ProtocolProviderService)sService);
+ }
+ else if (serviceEvent.getType() == ServiceEvent.UNREGISTERING)
+ {
+ this.handleProviderRemoved( (ProtocolProviderService) sService);
+ }
+
+ }
+
+ /**
+ * Used to attach the Call History Service to existing or
+ * just registered protocol provider. Checks if the provider has implementation
+ * of OperationSetBasicTelephony
+ *
+ * @param provider ProtocolProviderService
+ */
+ private void handleProviderAdded(ProtocolProviderService provider)
+ {
+ logger.debug("Adding protocol provider " + provider.getProtocolName());
+
+ // check whether the provider has a basic telephony operation set
+ OperationSetBasicTelephony opSetTelephony
+ = (OperationSetBasicTelephony) provider
+ .getSupportedOperationSets().get(
+ OperationSetBasicTelephony.class.getName());
+
+ if (opSetTelephony != null)
+ {
+ opSetTelephony.addCallListener(this);
+ }
+ else
+ {
+ logger.trace("Service did not have a basic telephony op. set.");
+ }
+ }
+
+ /**
+ * Removes the specified provider from the list of currently known providers
+ * and ignores all the calls made by it
+ *
+ * @param provider the ProtocolProviderService that has been unregistered.
+ */
+ private void handleProviderRemoved(ProtocolProviderService provider)
+ {
+ OperationSetBasicTelephony opSetTelephony
+ = (OperationSetBasicTelephony) provider
+ .getSupportedOperationSets().get(
+ OperationSetBasicTelephony.class.getName());
+
+ if (opSetTelephony != null)
+ {
+ opSetTelephony.removeCallListener(this);
+ }
+ }
+
+ /**
+ * Adding progress listener for monitoring progress of search process
+ *
+ * @param listener HistorySearchProgressListener
+ */
+ public void addSearchProgressListener(CallHistorySearchProgressListener
+ listener)
+ {
+ synchronized(progressListeners){
+ HistorySearchProgressListener wrapperListener =
+ new SearchProgressWrapper(listener);
+ progressListeners.put(listener, wrapperListener);
+ }
+ }
+
+ /**
+ * Removing progress listener
+ *
+ * @param listener HistorySearchProgressListener
+ */
+ public void removeSearchProgressListener(
+ CallHistorySearchProgressListener listener)
+ {
+ synchronized(progressListeners){
+ progressListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Add the registered CallHistorySearchProgressListeners
+ * to the given HistoryReader
+ *
+ * @param reader HistoryReader
+ * @param countContacts number of contacts will search
+ */
+ private void addHistorySearchProgressListeners(
+ HistoryReader reader, int countContacts)
+ {
+ synchronized(progressListeners)
+ {
+ Iterator iter = progressListeners.values().iterator();
+ while (iter.hasNext())
+ {
+ SearchProgressWrapper l =
+ (SearchProgressWrapper) iter.next();
+ l.contactCount = countContacts;
+ reader.addSearchProgressListener(l);
+ }
+ }
+ }
+
+ /**
+ * Removes the registered CallHistorySearchProgressListeners
+ * from the given HistoryReader
+ *
+ * @param reader HistoryReader
+ */
+ private void removeHistorySearchProgressListeners(HistoryReader reader)
+ {
+ synchronized(progressListeners)
+ {
+ Iterator iter = progressListeners.values().iterator();
+ while (iter.hasNext())
+ {
+ SearchProgressWrapper l =
+ (SearchProgressWrapper) iter.next();
+ l.clear();
+ reader.removeSearchProgressListener(l);
+ }
+ }
+ }
+
+ /**
+ * Gets all the history readers for the contacts in the given MetaContact
+ * @param contact MetaContact
+ * @return Hashtable
+ */
+ private Hashtable getHistoryReaders(MetaContact contact)
+ {
+ Hashtable readers = new Hashtable();
+ Iterator iter = contact.getContacts();
+ while (iter.hasNext())
+ {
+ Contact item = (Contact) iter.next();
+
+ try
+ {
+ History history = this.getHistory(null, item);
+ readers.put(item, history.getReader());
+ }
+ catch (IOException e)
+ {
+ logger.error("Could not read history", e);
+ }
+ }
+ return readers;
+ }
+
+ /**
+ * CallListener implementation for incoming calls
+ * @param event CallEvent
+ */
+ public void incomingCallReceived(CallEvent event)
+ {
+ handleNewCall(event.getSourceCall(), CallRecord.IN);
+ }
+
+ /**
+ * CallListener implementation for outgoing calls
+ * @param event CallEvent
+ */
+ public void outgoingCallCreated(CallEvent event)
+ {
+ logger.info("outgoingCallCreated");
+ handleNewCall(event.getSourceCall(), CallRecord.OUT);
+ }
+
+ /**
+ * CallListener implementation for call endings
+ * @param event CallEvent
+ */
+ public void callEnded(CallEvent event)
+ {
+ logger.info("callEnded");
+ Date endTime = new Date();
+
+ CallRecord callRecord = findCallRecord(event.getSourceCall());
+
+ // no such call
+ if (callRecord == null)
+ return;
+
+ callRecord.setEndTime(endTime);
+
+ writeCall(callRecord, null, null);
+
+ currentCallRecords.remove(callRecord);
+ }
+
+ /**
+ * Adding a record for joining participant
+ * @param callParticipant CallParticipant
+ */
+ private void handleParticipantAdded(CallParticipant callParticipant)
+ {
+ CallRecord callRecord = findCallRecord(callParticipant.getCall());
+
+ // no such call
+ if(callRecord == null)
+ return;
+
+ CallParticipantRecord newRec = new CallParticipantRecord(
+ callParticipant.getAddress(),
+ new Date(),
+ null);
+
+ callRecord.getParticipantRecords().add(newRec);
+ }
+
+ /**
+ * Adding a record for removing participant from call
+ * @param callParticipant CallParticipant
+ */
+ private void handleParticipantRemoved(CallParticipant callParticipant)
+ {
+ logger.info("handleParticipantRemoved");
+ CallRecord callRecord = findCallRecord(callParticipant.getCall());
+ String pAddress = callParticipant.getAddress();
+
+ CallParticipantRecord cpRecord =
+ callRecord.findParticipantRecord(pAddress);
+
+ // no such participant
+ if(cpRecord == null)
+ return;
+
+ cpRecord.setEndTime(new Date());
+ }
+
+ /**
+ * Finding a CallRecord for the given call
+ * @param call Call
+ * @return CallRecord
+ */
+ private CallRecord findCallRecord(Call call)
+ {
+ Iterator iter = currentCallRecords.iterator();
+ while (iter.hasNext())
+ {
+ CallRecord item = (CallRecord) iter.next();
+ if(item.getSourceCall().equals(call))
+ return item;
+ }
+
+ return null;
+ }
+
+ /**
+ * Adding a record for a new call
+ * @param sourceCall Call
+ * @param direction String
+ */
+ private void handleNewCall(Call sourceCall, String direction)
+ {
+ // if call exist. its not new
+ if(currentCallRecords.contains(sourceCall))
+ return;
+
+ CallRecord newRecord = new CallRecord(
+ sourceCall,
+ direction,
+ new Date(),
+ null);
+
+ sourceCall.addCallChangeListener(historyCallChangeListener);
+
+ currentCallRecords.add(newRecord);
+
+ // if has already perticipants Dispatch them
+ Iterator iter = sourceCall.getCallParticipants();
+ while (iter.hasNext())
+ {
+ CallParticipant item = (CallParticipant) iter.next();
+ handleParticipantAdded(item);
+ }
+ }
+
+ /**
+ * A wrapper around HistorySearchProgressListener
+ * that fires events for CallHistorySearchProgressListener
+ */
+ private class SearchProgressWrapper
+ implements HistorySearchProgressListener
+ {
+ private CallHistorySearchProgressListener listener = null;
+ int contactCount = 0;
+ int currentContactCount = 0;
+ int currentProgress = 0;
+ int lastHistoryProgress = 0;
+
+ SearchProgressWrapper(CallHistorySearchProgressListener listener)
+ {
+ this.listener = listener;
+ }
+
+ public void progressChanged(ProgressEvent evt)
+ {
+ int progress = getProgressMapping(evt.getProgress());
+
+ listener.progressChanged(
+ new net.java.sip.communicator.service.callhistory.event.
+ ProgressEvent(CallHistoryServiceImpl.this, evt, progress));
+ }
+
+ /**
+ * Calculates the progress according the count of the contacts
+ * we will search
+ * @param historyProgress int
+ * @return int
+ */
+ private int getProgressMapping(int historyProgress)
+ {
+ currentProgress += (historyProgress - lastHistoryProgress)/contactCount;
+
+ if(historyProgress == HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE)
+ {
+ currentContactCount++;
+ lastHistoryProgress = 0;
+
+ // this is the last one and the last event fire the max
+ // there will be looses in currentProgress due to the devision
+ if(currentContactCount == contactCount)
+ currentProgress =
+ CallHistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE;
+ }
+ else
+ lastHistoryProgress = historyProgress;
+
+ return currentProgress;
+ }
+
+ /**
+ * clear the values
+ */
+ void clear()
+ {
+ contactCount = 0;
+ currentProgress = 0;
+ lastHistoryProgress = 0;
+ currentContactCount = 0;
+ }
+ }
+
+
+
+ /**
+ * Used to compare CallRecords
+ * and to be ordered in TreeSet according their timestamp
+ */
+ private class CallRecordComparator
+ implements Comparator
+ {
+ public int compare(Object o1, Object o2)
+ {
+ return ((CallRecord)o1).getStartTime().
+ compareTo(((CallRecord)o2).getStartTime());
+ }
+ }
+
+ /**
+ * Receive events for adding or removing participants from a call
+ */
+ private class HistoryCallChangeListener
+ implements CallChangeListener
+ {
+ public void callParticipantAdded(CallParticipantEvent evt)
+ {
+ handleParticipantAdded(evt.getSourceCallParticipant());
+ }
+
+ public void callParticipantRemoved(CallParticipantEvent evt)
+ {
+ handleParticipantRemoved(evt.getSourceCallParticipant());
+ }
+
+ public void callStateChanged(CallChangeEvent evt)
+ {
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/callhistory/callhistory.manifest.mf b/src/net/java/sip/communicator/impl/callhistory/callhistory.manifest.mf
new file mode 100644
index 0000000..e50b60e
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/callhistory/callhistory.manifest.mf
@@ -0,0 +1,19 @@
+Bundle-Activator: net.java.sip.communicator.impl.callhistory.CallHistoryActivator
+Bundle-Name: Call History Service Provider
+Bundle-Description: A bundle that implements the call history package.
+Bundle-Vendor: sip-communicator.org
+Bundle-Version: 0.0.1
+Import-Package: org.ungoverned.gravity.servicebinder,
+ org.osgi.framework,
+ net.java.sip.communicator.service.fileaccess,
+ net.java.sip.communicator.service.history,
+ net.java.sip.communicator.service.history.event,
+ net.java.sip.communicator.service.contactlist,
+ net.java.sip.communicator.service.history.records,
+ net.java.sip.communicator.util,
+ net.java.sip.communicator.service.protocol,
+ net.java.sip.communicator.service.protocol.icqconstants,
+ net.java.sip.communicator.service.protocol.event,
+Export-Package: net.java.sip.communicator.service.callhistory,
+ net.java.sip.communicator.service.callhistory.event
+Metadata-Location: /net/java/sip/communicator/impl/msghistory/callhistory.metadata.xml
diff --git a/src/net/java/sip/communicator/impl/callhistory/callhistory.metadata.xml b/src/net/java/sip/communicator/impl/callhistory/callhistory.metadata.xml
new file mode 100755
index 0000000..75a2e9c
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/callhistory/callhistory.metadata.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<bundle>
+ <component class="net.java.sip.communicator.impl.msghistory.MessageHistoryServiceImpl">
+ <provides service="net.java.sip.communicator.service.msghistory.MessageHistoryService"/>
+
+ <requires service="net.java.sip.communicator.service.configuration.ConfigurationService"
+ filter=""
+ policy="static"
+ cardinality="1..1"
+ bind-method="setConfigurationService"
+ unbind-method="unsetConfigurationService" />
+
+ <requires service="net.java.sip.communicator.service.history.HistoryService"
+ filter=""
+ policy="static"
+ cardinality="1..1"
+ bind-method="setHistoryService"
+ unbind-method="unsetHistoryService" />
+
+ <requires service="net.java.sip.communicator.service.protocol.ProtocolProviderService"
+ filter=""
+ cardinality="1..n"
+ policy="dynamic"
+ bind-method="addProtocolProvider"
+ unbind-method="removeProtocolProvider" />
+
+ </component>
+</bundle>
diff --git a/src/net/java/sip/communicator/impl/protocol/mock/MockCall.java b/src/net/java/sip/communicator/impl/protocol/mock/MockCall.java
new file mode 100644
index 0000000..c87e0b1
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/mock/MockCall.java
@@ -0,0 +1,161 @@
+package net.java.sip.communicator.impl.protocol.mock;
+
+import java.util.*;
+
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.impl.protocol.sip.CallParticipantSipImpl;
+import net.java.sip.communicator.util.Logger;
+
+/**
+ * @author Damian Minkov
+ */
+public class MockCall
+ extends Call
+ implements CallParticipantListener
+{
+ private static final Logger logger = Logger.getLogger(MockCall.class);
+
+ /**
+ * A list containing all <tt>CallParticipant</tt>s of this call.
+ */
+ private Vector callParticipants = new Vector();
+
+ /**
+ * The state that this call is currently in.
+ */
+ private CallState callState = CallState.CALL_INITIALIZATION;
+
+
+ public MockCall(MockProvider sourceProvider)
+ {
+ super(sourceProvider);
+ }
+
+ /**
+ * Returns an iterator over all call participants.
+ *
+ * @return an Iterator over all participants currently involved in the
+ * call.
+ */
+ public Iterator getCallParticipants()
+ {
+ return callParticipants.iterator();
+ }
+
+ /**
+ * Returns the number of participants currently associated with this call.
+ *
+ * @return an <tt>int</tt> indicating the number of participants
+ * currently associated with this call.
+ */
+ public int getCallParticipantsCount()
+ {
+ return callParticipants.size();
+ }
+
+ /**
+ * Returns the state that this call is currently in.
+ *
+ * @return a reference to the <tt>CallState</tt> instance that the call
+ * is currently in.
+ */
+ public CallState getCallState()
+ {
+ return callState;
+ }
+
+ /**
+ * Adds <tt>callParticipant</tt> to the list of participants in this call.
+ * If the call participant is already included in the call, the method has
+ * no effect.
+ *
+ * @param callParticipant the new <tt>CallParticipant</tt>
+ */
+ public void addCallParticipant(MockCallParticipant callParticipant)
+ {
+ if(callParticipants.contains(callParticipant))
+ return;
+
+ callParticipant.addCallParticipantListener(this);
+
+ this.callParticipants.add(callParticipant);
+
+ logger.info("Will fire participant added");
+
+ fireCallParticipantEvent(
+ callParticipant, CallParticipantEvent.CALL_PARTICIPANT_ADDED);
+ }
+
+ /**
+ * Removes <tt>callParticipant</tt> from the list of participants in this
+ * call. The method has no effect if there was no such participant in the
+ * call.
+ *
+ * @param callParticipant the <tt>CallParticipant</tt> leaving the call;
+ */
+ public void removeCallParticipant(MockCallParticipant callParticipant)
+ {
+ if(!callParticipants.contains(callParticipant))
+ return;
+
+ this.callParticipants.remove(callParticipant);
+ callParticipant.removeCallParticipantListener(this);
+
+ fireCallParticipantEvent(
+ callParticipant, CallParticipantEvent.CALL_PARTICIPANT_REMVOVED);
+
+ if(callParticipants.size() == 0)
+ setCallState(CallState.CALL_ENDED);
+ }
+
+ /**
+ * Sets the state of this call and fires a call change event notifying
+ * registered listenres for the change.
+ *
+ * @param newState a reference to the <tt>CallState</tt> instance that
+ * the call is to enter.
+ */
+ public void setCallState(CallState newState)
+ {
+ CallState oldState = getCallState();
+
+ if(oldState == newState)
+ return;
+
+ this.callState = newState;
+
+ fireCallChangeEvent(
+ CallChangeEvent.CALL_STATE_CHANGE, oldState, newState);
+ }
+
+ public void participantStateChanged(CallParticipantChangeEvent evt)
+ {
+ if ( ( (CallParticipantState) evt.getNewValue())
+ == CallParticipantState.DISCONNECTED
+ || ( (CallParticipantState) evt.getNewValue())
+ == CallParticipantState.FAILED)
+ {
+ removeCallParticipant(
+ (MockCallParticipant) evt.getSourceCallParticipant());
+ }
+ else if ( ( (CallParticipantState) evt.getNewValue())
+ == CallParticipantState.CONNECTED
+ && getCallState().equals(CallState.CALL_INITIALIZATION))
+ {
+ setCallState(CallState.CALL_IN_PROGRESS);
+ }
+ }
+
+ public void participantDisplayNameChanged(CallParticipantChangeEvent evt)
+ {
+ }
+
+ public void participantAddressChanged(CallParticipantChangeEvent evt)
+ {
+ }
+
+ public void participantImageChanged(CallParticipantChangeEvent evt)
+ {
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/mock/MockCallParticipant.java b/src/net/java/sip/communicator/impl/protocol/mock/MockCallParticipant.java
new file mode 100644
index 0000000..ac1e57e
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/mock/MockCallParticipant.java
@@ -0,0 +1,160 @@
+package net.java.sip.communicator.impl.protocol.mock;
+
+import java.util.*;
+
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.CallParticipantChangeEvent;
+
+/**
+ * <p> </p>
+ *
+ * <p> </p>
+ *
+ * <p> </p>
+ *
+ * <p> </p>
+ *
+ * @author Damian Minkov
+ */
+public class MockCallParticipant
+ extends AbstractCallParticipant
+{
+ /**
+ * The sip address of this participant
+ */
+ private String participantAddress = null;
+
+ /**
+ * The call participant belongs to.
+ */
+ private MockCall call;
+
+ /**
+ * A string uniquely identifying the participant.
+ */
+ private String participantID;
+
+ /**
+ * Indicates the date when is call participant passed into its current state.
+ */
+ protected Date currentStateStartDate = new Date();
+
+ /**
+ * The state of the call participant.
+ */
+ protected CallParticipantState callParticipantState =
+ CallParticipantState.UNKNOWN;
+
+
+ public MockCallParticipant(String address, MockCall owningCall)
+ {
+ this.participantAddress = address;
+ this.call = owningCall;
+
+ call.addCallParticipant(this);
+
+ //create the uid
+ this.participantID = String.valueOf( System.currentTimeMillis())
+ + String.valueOf(hashCode());
+ }
+
+ /**
+ * Returns a String locator for that participant.
+ *
+ * @return the participant's address or phone number.
+ */
+ public String getAddress()
+ {
+ return participantAddress;
+ }
+
+ /**
+ * Returns a reference to the call that this participant belongs to.
+ *
+ * @return a reference to the call containing this participant.
+ */
+ public Call getCall()
+ {
+ return call;
+ }
+
+ /**
+ * Returns the date (time) when this call participant acquired its
+ * current status.
+ *
+ * @return a java.util.Date object containing the date when this call
+ * participant entered its current state.
+ */
+ public Date getCurrentStateStartDate()
+ {
+ return currentStateStartDate;
+ }
+
+ /**
+ * Returns a human readable name representing this participant.
+ *
+ * @return a String containing a name for that participant.
+ */
+ public String getDisplayName()
+ {
+ return participantAddress;
+ }
+
+ /**
+ * The method returns an image representation of the call participant
+ * (e.g.
+ *
+ * @return byte[] a byte array containing the image or null if no image
+ * is available.
+ */
+ public byte[] getImage()
+ {
+ return null;
+ }
+
+ /**
+ * Returns a unique identifier representing this participant.
+ *
+ * @return an identifier representing this call participant.
+ */
+ public String getParticipantID()
+ {
+ return participantID;
+ }
+
+ /**
+ * Returns an object representing the current state of that participant.
+ *
+ * @return a CallParticipantState instance representin the participant's
+ * state.
+ */
+ public CallParticipantState getState()
+ {
+ return callParticipantState;
+ }
+
+ /**
+ * Causes this CallParticipant to enter the specified state. The method also
+ * sets the currentStateStartDate field and fires a
+ * CallParticipantChangeEvent.
+ *
+ * @param newState the state this call participant should enter.
+ * @param reason a string that could be set to contain a human readable
+ * explanation for the transition (particularly handy when moving into a
+ * FAILED state).
+ */
+ protected void setState(CallParticipantState newState, String reason)
+ {
+ CallParticipantState oldState = getState();
+
+ if(oldState == newState)
+ return;
+
+ this.callParticipantState = newState;
+ this.currentStateStartDate = new Date();
+ fireCallParticipantChangeEvent(
+ CallParticipantChangeEvent.CALL_PARTICIPANT_STATE_CHANGE,
+ oldState,
+ newState);
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java b/src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java
new file mode 100644
index 0000000..f5f7263
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java
@@ -0,0 +1,284 @@
+package net.java.sip.communicator.impl.protocol.mock;
+
+import java.text.*;
+import java.util.*;
+
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * A mock implementation of a basic telephony opearation set
+ *
+ * @author Damian Minkov
+ */
+public class MockOperationSetBasicTelephony
+ implements OperationSetBasicTelephony,
+ CallChangeListener
+{
+ private static final Logger logger
+ = Logger.getLogger(MockOperationSetBasicTelephony.class);
+
+ /**
+ * A list of listeners registered for
+ * <tt>CallEvent</tt>s.
+ */
+ private Vector callListeners = new Vector();
+
+ /**
+ * A reference to the <tt>ProtocolProviderServiceSipImpl</tt> instance
+ * that created us.
+ */
+ private MockProvider protocolProvider = null;
+
+ /**
+ * A table mapping call ids against call instances.
+ */
+ private Hashtable activeCalls = new Hashtable();
+
+
+ public MockOperationSetBasicTelephony(MockProvider protocolProvider)
+ {
+ this.protocolProvider = protocolProvider;
+ }
+
+ /**
+ * Registers the specified CallListener with this provider so that it
+ * could be notified when incoming calls are received.
+ *
+ * @param listener the listener to register with this provider.
+ */
+ public void addCallListener(CallListener listener)
+ {
+ synchronized(callListeners){
+ callListeners.add(listener);
+ }
+ }
+
+ /**
+ * Indicates a user request to answer an incoming call from the specified
+ * CallParticipant.
+ *
+ * @param participant the call participant that we'd like to anwer.
+ * @throws OperationFailedException with the corresponding code if we
+ * encounter an error while performing this operation.
+ */
+ public void answerCallParticipant(CallParticipant participant) throws
+ OperationFailedException
+ {
+ MockCallParticipant callParticipant
+ = (MockCallParticipant)participant;
+ if(participant.getState().equals(CallParticipantState.CONNECTED))
+ {
+ logger.info("Ignoring user request to answer a CallParticipant "
+ + "that is already connected. CP:" + participant);
+ return;
+ }
+
+ callParticipant.setState(CallParticipantState.CONNECTED, null);
+ }
+
+ /**
+ * Create a new call and invite the specified CallParticipant to it.
+ *
+ * @param uri the address of the callee that we should invite to a new
+ * call.
+ * @return CallParticipant the CallParticipant that will represented by
+ * the specified uri. All following state change events will be
+ * delivered through that call participant. The Call that this
+ * participant is a member of could be retrieved from the
+ * CallParticipatn instance with the use of the corresponding method.
+ * @throws OperationFailedException with the corresponding code if we
+ * fail to create the call.
+ * @throws ParseException if <tt>callee</tt> is not a valid sip address
+ * string.
+ */
+ public Call createCall(String uri) throws OperationFailedException,
+ ParseException
+ {
+ return createNewCall(uri);
+ }
+
+ /**
+ * Create a new call and invite the specified CallParticipant to it.
+ *
+ * @param callee the address of the callee that we should invite to a
+ * new call.
+ * @return CallParticipant the CallParticipant that will represented by
+ * the specified uri. All following state change events will be
+ * delivered through that call participant. The Call that this
+ * participant is a member of could be retrieved from the
+ * CallParticipatn instance with the use of the corresponding method.
+ * @throws OperationFailedException with the corresponding code if we
+ * fail to create the call.
+ */
+ public Call createCall(Contact callee) throws OperationFailedException
+ {
+ return createNewCall(callee.getAddress());
+ }
+
+ private Call createNewCall(String address)
+ {
+ MockCall newCall = new MockCall(protocolProvider);
+
+ newCall.addCallChangeListener(this);
+ activeCalls.put(newCall.getCallID(), newCall);
+
+ new MockCallParticipant(address, newCall);
+
+ return newCall;
+ }
+
+ /**
+ * Returns an iterator over all currently active calls.
+ *
+ * @return Iterator
+ */
+ public Iterator getActiveCalls()
+ {
+ return activeCalls.values().iterator();
+ }
+
+ /**
+ * Indicates a user request to end a call with the specified call
+ * particiapnt.
+ *
+ * @param participant the participant that we'd like to hang up on.
+ * @throws OperationFailedException with the corresponding code if we
+ * encounter an error while performing this operation.
+ */
+ public void hangupCallParticipant(CallParticipant participant) throws
+ OperationFailedException
+ {
+ //do nothing if the call is already ended
+ if (participant.getState().equals(CallParticipantState.DISCONNECTED))
+ {
+ logger.debug("Ignoring a request to hangup a call participant "
+ +"that is already DISCONNECTED");
+ return;
+ }
+
+ MockCallParticipant callParticipant
+ = (MockCallParticipant)participant;
+
+ logger.info("hangupCallParticipant");
+ callParticipant.setState(CallParticipantState.DISCONNECTED, null);
+ }
+
+ /**
+ * Resumes communication with a call participant previously put on hold.
+ *
+ * @param participant the call participant to put on hold.
+ * @todo Implement this
+ * net.java.sip.communicator.service.protocol.OperationSetBasicTelephony
+ * method
+ */
+ public void putOffHold(CallParticipant participant)
+ {
+
+ }
+
+ /**
+ * Puts the specified CallParticipant "on hold".
+ *
+ * @param participant the participant that we'd like to put on hold.
+ * @throws OperationFailedException with the corresponding code if we
+ * encounter an error while performing this operation.
+ * @todo Implement this
+ * net.java.sip.communicator.service.protocol.OperationSetBasicTelephony
+ * method
+ */
+ public void putOnHold(CallParticipant participant) throws
+ OperationFailedException
+ {
+ }
+
+ /**
+ * Removes the specified listener from the list of call listeners.
+ *
+ * @param listener the listener to unregister.
+ */
+ public void removeCallListener(CallListener listener)
+ {
+ synchronized(callListeners){
+ callListeners.remove(listener);
+ }
+ }
+
+ public Call receiveCall(String fromAddress)
+ throws Exception
+ {
+ Call newCall = createCall(fromAddress);
+ fireCallEvent(CallEvent.CALL_RECEIVED, newCall);
+
+ return newCall;
+ }
+
+ public Call placeCall(String toAddress)
+ throws Exception
+ {
+ Call newCall = createCall(toAddress);
+ fireCallEvent(CallEvent.CALL_INITIATED, newCall);
+ return newCall;
+ }
+
+ /**
+ * Creates and dispatches a <tt>CallEvent</tt> notifying registered
+ * listeners that an event with id <tt>eventID</tt> has occurred on
+ * <tt>sourceCall</tt>.
+ *
+ * @param eventID the ID of the event to dispatch
+ * @param sourceCall the call on which the event has occurred.
+ */
+ protected void fireCallEvent( int eventID,
+ Call sourceCall)
+ {
+ CallEvent cEvent = new CallEvent(sourceCall, eventID);
+
+ logger.debug("Dispatching a CallEvent to "
+ + callListeners.size()
+ +" listeners. event is: " + cEvent.toString());
+
+ Iterator listeners = new ArrayList(callListeners).iterator();
+
+ while(listeners.hasNext())
+ {
+ CallListener listener = (CallListener)listeners.next();
+
+ if(eventID == CallEvent.CALL_INITIATED)
+ listener.outgoingCallCreated(cEvent);
+ else if(eventID == CallEvent.CALL_RECEIVED)
+ listener.incomingCallReceived(cEvent);
+ else if(eventID == CallEvent.CALL_ENDED)
+ listener.callEnded(cEvent);
+ }
+ }
+
+ public CallParticipant addNewCallParticipant(Call call, String address)
+ {
+ return new MockCallParticipant(address, (MockCall)call);
+ }
+
+ public void callParticipantAdded(CallParticipantEvent evt)
+ {
+ }
+
+ public void callParticipantRemoved(CallParticipantEvent evt)
+ {
+ }
+
+ public void callStateChanged(CallChangeEvent evt)
+ {
+ if(evt.getEventType().equals(CallChangeEvent.CALL_STATE_CHANGE)
+ && ((CallState)evt.getNewValue()).equals(CallState.CALL_ENDED))
+ {
+ MockCall sourceCall = (MockCall)this.activeCalls
+ .remove(evt.getSourceCall().getCallID());
+
+ logger.trace( "Removing call " + sourceCall + " from the list of "
+ + "active calls because it entered an ENDED state");
+
+ fireCallEvent(CallEvent.CALL_ENDED, sourceCall);
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/service/callhistory/CallHistoryService.java b/src/net/java/sip/communicator/service/callhistory/CallHistoryService.java
index 6e26a71..01153b9 100644
--- a/src/net/java/sip/communicator/service/callhistory/CallHistoryService.java
+++ b/src/net/java/sip/communicator/service/callhistory/CallHistoryService.java
@@ -9,6 +9,7 @@ package net.java.sip.communicator.service.callhistory;
import java.util.*;
import net.java.sip.communicator.service.contactlist.*;
+import net.java.sip.communicator.service.callhistory.event.*;
/**
* The Call History Service stores info about calls made from various protocols
@@ -55,6 +56,38 @@ public interface CallHistoryService
Collection findByPeriod(MetaContact contact, Date startDate, Date endDate)
throws RuntimeException;
+
+ /**
+ * Returns all the calls made after the given date
+ *
+ * @param startDate Date the start date of the calls
+ * @return Collection of CallReceivedEvent
+ * @throws RuntimeException
+ */
+ Collection findByStartDate(Date startDate)
+ throws RuntimeException;
+
+ /**
+ * Returns all the calls made before the given date
+ *
+ * @param endDate Date the end date of the calls
+ * @return Collection of CallReceivedEvent
+ * @throws RuntimeException
+ */
+ Collection findByEndDate(Date endDate)
+ throws RuntimeException;
+
+ /**
+ * Returns all the calls made between the given dates
+ *
+ * @param startDate Date the start date of the calls
+ * @param endDate Date the end date of the calls
+ * @return Collection of CallReceivedEvent
+ * @throws RuntimeException
+ */
+ Collection findByPeriod(Date startDate, Date endDate)
+ throws RuntimeException;
+
/**
* Returns the supplied number of recent calls made by all the contacts
* in the supplied metacontact
@@ -67,4 +100,29 @@ public interface CallHistoryService
Collection findLast(MetaContact contact, int count)
throws RuntimeException;
+ /**
+ * Returns the supplied number of recent calls made by all the contacts
+ * in the supplied metacontact
+ *
+ * @param count calls count
+ * @return Collection of CallReceivedEvent
+ * @throws RuntimeException
+ */
+ Collection findLast(int count)
+ throws RuntimeException;
+
+
+ /**
+ * Adding progress listener for monitoring progress of search process
+ *
+ * @param listener HistorySearchProgressListener
+ */
+ void addSearchProgressListener(CallHistorySearchProgressListener listener);
+
+ /**
+ * Removing progress listener
+ *
+ * @param listener HistorySearchProgressListener
+ */
+ void removeSearchProgressListener(CallHistorySearchProgressListener listener);
}
diff --git a/src/net/java/sip/communicator/service/callhistory/CallParticipantRecord.java b/src/net/java/sip/communicator/service/callhistory/CallParticipantRecord.java
new file mode 100644
index 0000000..942c929
--- /dev/null
+++ b/src/net/java/sip/communicator/service/callhistory/CallParticipantRecord.java
@@ -0,0 +1,88 @@
+package net.java.sip.communicator.service.callhistory;
+
+import java.util.*;
+
+/**
+ * Structure used for encapsulating data when writing or reading
+ * Call History Data. Also These records are uesd for returning data
+ * from the Call History Service
+ *
+ * @author Damian Minkov
+ */
+public class CallParticipantRecord
+{
+ private String participantAddress = null;
+ private Date startTime = null;
+ private Date endTime = null;
+
+ /**
+ * Creates CallParticipantRecord
+ * @param participantAddress String
+ * @param startTime Date
+ * @param endTime Date
+ */
+ public CallParticipantRecord(
+ String participantAddress,
+ Date startTime,
+ Date endTime)
+ {
+ this.participantAddress = participantAddress;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ }
+
+ /**
+ * When participant diconnected from the call
+ *
+ * @return Date
+ */
+ public Date getEndTime()
+ {
+ return endTime;
+ }
+
+ /**
+ * The participant address
+ * @return String
+ */
+ public String getParticipantAddress()
+ {
+ return participantAddress;
+ }
+
+ /**
+ * When participant connected to the call
+ * @return Date
+ */
+ public Date getStartTime()
+ {
+ return startTime;
+ }
+
+ /**
+ * Sets the time the participant joined the call
+ * @param startTime Date
+ */
+ public void setStartTime(Date startTime)
+ {
+ this.startTime = startTime;
+ }
+
+ /**
+ * Sets the particiapnts address
+ * @param participantAddress String
+ */
+ public void setParticipantAddress(String participantAddress)
+ {
+ this.participantAddress = participantAddress;
+ }
+
+ /**
+ * Sets the time participant leaves the call
+ * @param endTime Date
+ */
+ public void setEndTime(Date endTime)
+ {
+ this.endTime = endTime;
+ }
+}
diff --git a/src/net/java/sip/communicator/service/callhistory/CallRecord.java b/src/net/java/sip/communicator/service/callhistory/CallRecord.java
new file mode 100644
index 0000000..af6f069
--- /dev/null
+++ b/src/net/java/sip/communicator/service/callhistory/CallRecord.java
@@ -0,0 +1,163 @@
+package net.java.sip.communicator.service.callhistory;
+
+import java.util.*;
+
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * Structure used for encapsulating data when writing or reading
+ * Call History Data. Also These records are uesd for returning data
+ * from the Call History Service
+ *
+ * @author Damian Minkov
+ */
+public class CallRecord
+{
+ /**
+ * Possible directions of the call
+ */
+ public final static String OUT = "out";
+ public final static String IN = "in";
+
+ private Call sourceCall = null;
+ private String direction = null;
+ private Vector participantRecords = new Vector();
+ private Date startTime = null;
+ private Date endTime = null;
+
+ /**
+ * Creates CallRecord
+ */
+ public CallRecord()
+ {
+ }
+
+ /**
+ * Creates Call Record
+ * @param sourceCall Call
+ * @param direction String
+ * @param startTime Date
+ * @param endTime Date
+ */
+ public CallRecord(
+ Call sourceCall,
+ String direction,
+ Date startTime,
+ Date endTime)
+ {
+ this.sourceCall = sourceCall;
+ this.direction = direction;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ }
+
+ /**
+ * Finds a Participant with the supplied address
+ * @param address String
+ * @return CallParticipantRecord
+ */
+ public CallParticipantRecord findParticipantRecord(String address)
+ {
+ Iterator iter = participantRecords.iterator();
+ while (iter.hasNext())
+ {
+ CallParticipantRecord item = (CallParticipantRecord) iter.next();
+ if (item.getParticipantAddress().equals(address))
+ return item;
+ }
+
+ return null;
+ }
+
+ /**
+ * Set the time when the call finishes
+ * If some participant has no end Time set we set it also
+ * @param endTime Date
+ */
+ public void setEndTime(Date endTime)
+ {
+ this.endTime = endTime;
+
+ Iterator iter = participantRecords.iterator();
+ while (iter.hasNext())
+ {
+ CallParticipantRecord item = (CallParticipantRecord) iter.next();
+ if(item.getEndTime() == null)
+ item.setEndTime(endTime);
+ }
+ }
+
+ /**
+ * Sets the time when the call begins
+ * @param startTime Date
+ */
+ public void setStartTime(Date startTime)
+ {
+ this.startTime = startTime;
+ }
+
+ /**
+ * The source call which this record servers
+ * @param sourceCall Call
+ */
+ public void setSourceCall(Call sourceCall)
+ {
+ this.sourceCall = sourceCall;
+ }
+
+ /**
+ * Sets the direction of the call
+ * IN or OUT
+ * @param direction String
+ */
+ public void setDirection(String direction)
+ {
+ this.direction = direction;
+ }
+
+ /**
+ * Returns the direction of the call
+ * IN or OUT
+ * @return String
+ */
+ public String getDirection()
+ {
+ return direction;
+ }
+
+ /**
+ * Returns the time when the call has finished
+ * @return Date
+ */
+ public Date getEndTime()
+ {
+ return endTime;
+ }
+
+ /**
+ * Return Vector of CallParticipantRecords
+ * @return Vector
+ */
+ public Vector getParticipantRecords()
+ {
+ return participantRecords;
+ }
+
+ /**
+ * The Call source of this record
+ * @return Call
+ */
+ public Call getSourceCall()
+ {
+ return sourceCall;
+ }
+
+ /**
+ * The time when the call has began
+ * @return Date
+ */
+ public Date getStartTime()
+ {
+ return startTime;
+ }
+}
diff --git a/src/net/java/sip/communicator/service/callhistory/event/CallHistorySearchProgressListener.java b/src/net/java/sip/communicator/service/callhistory/event/CallHistorySearchProgressListener.java
new file mode 100644
index 0000000..579d400
--- /dev/null
+++ b/src/net/java/sip/communicator/service/callhistory/event/CallHistorySearchProgressListener.java
@@ -0,0 +1,40 @@
+/*
+ * 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.callhistory.event;
+
+import net.java.sip.communicator.service.history.event.*;
+
+/**
+ * When searching into the call history a ProgressEvent is fired whenever
+ * the progress is changed. Its fired through the search process
+ * informing us about the current progress.
+ *
+ * @author Damian Minkov
+ */
+public interface CallHistorySearchProgressListener
+{
+ /**
+ * The minimum value for the progress change.
+ * This is value indicates that the process has started.
+ */
+ public static int PROGRESS_MINIMUM_VALUE =
+ HistorySearchProgressListener.PROGRESS_MINIMUM_VALUE;
+
+ /**
+ * The maximum value for the progress change.
+ * This is value indicates that the process is finished.
+ */
+ public static int PROGRESS_MAXIMUM_VALUE =
+ HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE;
+
+ /**
+ * This method gets called when progress changes through the search process
+ * @param evt ProgressEvent the event holding the search condition and
+ * the current progress value.
+ */
+ void progressChanged(ProgressEvent evt);
+}
diff --git a/src/net/java/sip/communicator/service/callhistory/event/ProgressEvent.java b/src/net/java/sip/communicator/service/callhistory/event/ProgressEvent.java
new file mode 100644
index 0000000..ed8e58f
--- /dev/null
+++ b/src/net/java/sip/communicator/service/callhistory/event/ProgressEvent.java
@@ -0,0 +1,74 @@
+/*
+ * 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.callhistory.event;
+
+import java.util.*;
+
+/**
+ * A "ProgressEvent" event gets delivered through the search process
+ * of CallHistoryService Service.
+ * The event is wrapper around the generated event from the History Service
+ *
+ * @author Damian Minkov
+ */
+public class ProgressEvent
+ extends java.util.EventObject
+{
+ private net.java.sip.communicator.service.history.event.ProgressEvent evt;
+
+ /**
+ * The current progress that we will pass.
+ */
+ private int progress = 0;
+
+ public ProgressEvent(
+ Object source,
+ net.java.sip.communicator.service.history.event.ProgressEvent evt,
+ int progress)
+ {
+ super(source);
+
+ this.evt = evt;
+ this.progress = progress;
+ }
+
+ /**
+ * Gets the current progress that will be fired.
+ * @return int the progress value
+ */
+ public int getProgress()
+ {
+ return progress;
+ }
+
+ /**
+ * The end date in the search condition.
+ * @return Date end date value
+ */
+ public Date getEndDate()
+ {
+ return evt.getEndDate();
+ }
+
+ /**
+ * The start date in the search condition.
+ * @return Date start date value
+ */
+ public Date getStartDate()
+ {
+ return evt.getStartDate();
+ }
+
+ /**
+ * Sets the progress that will be fired
+ * @param progress int progress value
+ */
+ public void setProgress(int progress)
+ {
+ this.progress = progress;
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/callhistory/CallHistoryServiceLick.java b/test/net/java/sip/communicator/slick/callhistory/CallHistoryServiceLick.java
new file mode 100644
index 0000000..8ccea16
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/callhistory/CallHistoryServiceLick.java
@@ -0,0 +1,56 @@
+/*
+ * 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.slick.callhistory;
+
+import java.util.*;
+
+import org.osgi.framework.*;
+import junit.framework.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ *
+ * @author Damian Minkov
+ */
+public class CallHistoryServiceLick extends TestSuite implements BundleActivator {
+ private static Logger logger = Logger.getLogger(CallHistoryServiceLick.class);
+
+ protected static BundleContext bc = null;
+
+ /**
+ * Start the History Sevice Implementation Compatibility Kit.
+ *
+ * @param bundleContext
+ * BundleContext
+ * @throws Exception
+ */
+ public void start(BundleContext bundleContext)
+ throws Exception
+ {
+ CallHistoryServiceLick.bc = bundleContext;
+
+ setName("CallHistoryServiceLick");
+ Hashtable properties = new Hashtable();
+ properties.put("service.pid", getName());
+
+ addTest(TestCallHistoryService.suite());
+ bundleContext.registerService(getClass().getName(), this, properties);
+
+ logger.debug("Successfully registered " + getClass().getName());
+ }
+
+ /**
+ * stop
+ *
+ * @param bundlecontext BundleContext
+ * @throws Exception
+ */
+ public void stop(BundleContext bundlecontext)
+ throws Exception
+ {
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/callhistory/TestCallHistoryService.java b/test/net/java/sip/communicator/slick/callhistory/TestCallHistoryService.java
new file mode 100644
index 0000000..9766180
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/callhistory/TestCallHistoryService.java
@@ -0,0 +1,353 @@
+/*
+ * 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.slick.callhistory;
+
+import java.util.*;
+
+import org.osgi.framework.*;
+import junit.framework.*;
+import net.java.sip.communicator.impl.protocol.mock.*;
+import net.java.sip.communicator.service.callhistory.*;
+import net.java.sip.communicator.service.contactlist.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Tests call history.
+ * First installs the MockProtocolProvider to be able to create som calls
+ * The call history service stores them
+ * and then tests the verious find methods - does they find the calls we have
+ * already made
+ *
+ * @author Damian Minkov
+ */
+public class TestCallHistoryService
+ extends TestCase
+{
+ private static final Logger logger = Logger.getLogger(TestCallHistoryService.class);
+
+ /**
+ * The provider that we use to make a dummy server-stored contactlist
+ * used for testing. The mockProvider is instantiated and registered
+ * by the metacontactlist slick activator.
+ */
+ public static MockProvider mockProvider = null;
+
+ public static MockOperationSetBasicTelephony mockBTelphonyOpSet = null;
+
+ private static ServiceReference callHistoryServiceRef = null;
+ public static CallHistoryService callHistoryService = null;
+
+ /**
+ * A reference to the registration of the first mock provider.
+ */
+ public static ServiceRegistration mockPrServiceRegistration = null;
+
+ private static Date controlDate1 = null;
+ private static Date controlDate2 = null;
+
+ /**
+ * The addresses will be used in the generated mock calls
+ */
+ private static Vector participantAddresses = new Vector();
+
+ public TestCallHistoryService(String name)
+ {
+ super(name);
+ }
+
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite();
+ suite.addTest(
+ new TestCallHistoryService("setupContact"));
+ suite.addTest(
+ new TestCallHistoryService("writeRecords"));
+ suite.addTest(
+ new TestCallHistoryService("readRecords"));
+ suite.addTest(
+ new TestCallHistoryService("checkRecordCompleteness"));
+
+ return suite;
+ }
+
+ protected void setUp() throws Exception
+ {
+ }
+
+ protected void tearDown() throws Exception
+ {
+ }
+
+ public void setupContact()
+ {
+ // changes the history service target derictory
+ System.setProperty("HistoryServiceDirectory", "test-callhistory");
+
+ mockProvider = new MockProvider("CallHistoryMockUser");
+
+ //store thre presence op set of the new provider into the fixture
+ Map supportedOperationSets =
+ mockProvider.getSupportedOperationSets();
+
+ mockBTelphonyOpSet =
+ (MockOperationSetBasicTelephony) supportedOperationSets.get(
+ OperationSetBasicTelephony.class.getName());
+
+ System.setProperty(MetaContactListService.PROVIDER_MASK_PROPERTY, "1");
+
+ Hashtable mockProvProperties = new Hashtable();
+ mockProvProperties.put(ProtocolProviderFactory.PROTOCOL
+ , mockProvider.getProtocolName());
+ mockProvProperties.put(MetaContactListService.PROVIDER_MASK_PROPERTY,
+ "1");
+
+ mockPrServiceRegistration =
+ CallHistoryServiceLick.bc.registerService(
+ ProtocolProviderService.class.getName(),
+ mockProvider,
+ mockProvProperties);
+ logger.debug("Registered a mock protocol provider! ");
+
+ callHistoryServiceRef =
+ CallHistoryServiceLick.bc.
+ getServiceReference(CallHistoryService.class.getName());
+
+ callHistoryService =
+ (CallHistoryService) CallHistoryServiceLick.bc.
+ getService(callHistoryServiceRef);
+
+ // Will genarate 4 Calls with 4 different participants
+ participantAddresses.add("participant_address_1");
+ participantAddresses.add("participant_address_2");
+ participantAddresses.add("participant_address_3");
+ participantAddresses.add("participant_address_4");
+ }
+
+ /**
+ * First create calls
+ */
+ public void writeRecords()
+ {
+ logger.info("write records ");
+
+ generateCall((String)participantAddresses.get(0));
+
+ controlDate1 = new Date();
+
+ generateCall((String)participantAddresses.get(1));
+
+ generateCall((String)participantAddresses.get(2));
+
+ controlDate2 = new Date();
+
+ generateCall((String)participantAddresses.get(3));
+ }
+
+ private void generateCall(String participant)
+ {
+ try
+ {
+ Call newCall = mockBTelphonyOpSet.placeCall(participant);
+
+ Vector v = new Vector();
+
+ Iterator iter = newCall.getCallParticipants();
+ while (iter.hasNext())
+ {
+ CallParticipant item = (CallParticipant) iter.next();
+ v.add(item);
+ }
+
+ waitSeconds(2000);
+
+ iter = v.iterator();
+ while (iter.hasNext())
+ {
+ CallParticipant item = (CallParticipant) iter.next();
+ mockBTelphonyOpSet.hangupCallParticipant(item);
+ }
+ }
+ catch (Exception ex1)
+ {
+ logger.error("Cannot place mock call", ex1);
+ fail("Cannot place mock call to " + participant);
+ }
+ }
+
+
+ private void waitSeconds(long secs)
+ {
+ Object lock = new Object();
+ synchronized (lock){
+ // wait a moment
+ try{
+ lock.wait(secs);
+ }
+ catch (InterruptedException ex){}
+ }
+ }
+
+ /**
+ * tests all read methods (finders)
+ */
+ public void readRecords()
+ {
+ /**
+ * This must match also many calls, as tests are run many times
+ * but the minimum is 3
+ */
+ Collection rs = callHistoryService.findByEndDate(controlDate2);
+ Iterator resultIter = rs.iterator();
+
+ assertTrue("Calls too few - findByEndDate", rs.size() >= 3);
+
+ /**
+ * must find 2 calls
+ */
+ rs = callHistoryService.findByPeriod(controlDate1, controlDate2);
+ resultIter = rs.iterator();
+
+ assertEquals("Calls must be 2", rs.size(), 2);
+
+ CallRecord rec = (CallRecord)resultIter.next();
+ CallParticipantRecord participant =
+ (CallParticipantRecord)rec.getParticipantRecords().get(0);
+
+ assertTrue("Participant incorrect ",
+ participant.getParticipantAddress().
+ equals(participantAddresses.get(1)));
+
+ rec = (CallRecord)resultIter.next();
+ participant = (CallParticipantRecord)rec.getParticipantRecords().get(0);
+
+ assertTrue("Participant incorrect ",
+ participant.getParticipantAddress().
+ equals(participantAddresses.get(2)));
+
+ /**
+ * must find 1 record
+ */
+ rs = callHistoryService.findByStartDate(controlDate2);
+ resultIter = rs.iterator();
+
+ assertEquals("Calls must be 1", rs.size(), 1);
+
+ rec = (CallRecord)resultIter.next();
+ participant = (CallParticipantRecord)rec.getParticipantRecords().get(0);
+
+ assertTrue("Participant incorrect ",
+ participant.getParticipantAddress().
+ equals(participantAddresses.get(3)));
+
+ /**
+ * Must return exactly the last 3 calls
+ */
+ rs = callHistoryService.findLast(3);
+ resultIter = rs.iterator();
+
+ assertEquals("Calls must be 3", rs.size(), 3);
+
+ rec = (CallRecord)resultIter.next();
+ participant = (CallParticipantRecord) rec.getParticipantRecords().get(0);
+
+ assertTrue("Participant incorrect ",
+ participant.getParticipantAddress().
+ equals(participantAddresses.get(1)));
+
+ rec = (CallRecord)resultIter.next();
+ participant = (CallParticipantRecord) rec.getParticipantRecords().get(0);
+
+ assertTrue("Participant incorrect ",
+ participant.getParticipantAddress().
+ equals(participantAddresses.get(2)));
+
+ rec = (CallRecord)resultIter.next();
+ participant = (CallParticipantRecord) rec.getParticipantRecords().get(0);
+
+ assertTrue("Participant incorrect ",
+ participant.getParticipantAddress().
+ equals(participantAddresses.get(3)));
+ }
+
+ public void checkRecordCompleteness()
+ {
+ Vector participantAddresses = new Vector();
+ participantAddresses.add("some_address");
+ participantAddresses.add("some_new_address");
+
+ try
+ {
+ Call newCall =
+ mockBTelphonyOpSet.placeCall((String)participantAddresses.get(0));
+
+ Vector v = new Vector();
+
+ Iterator iter = newCall.getCallParticipants();
+ while (iter.hasNext())
+ {
+ CallParticipant item = (CallParticipant) iter.next();
+ v.add(item);
+ }
+
+ waitSeconds(2000);
+
+ CallParticipant newParticipant =
+ mockBTelphonyOpSet.addNewCallParticipant(newCall,
+ (String)participantAddresses.get(1));
+
+ mockBTelphonyOpSet.hangupCallParticipant(newParticipant);
+
+ waitSeconds(2000);
+
+ iter = v.iterator();
+ while (iter.hasNext())
+ {
+ CallParticipant item = (CallParticipant) iter.next();
+ mockBTelphonyOpSet.hangupCallParticipant(item);
+ }
+ }
+ catch (Exception ex1)
+ {
+ logger.error("Cannot place mock call", ex1);
+ fail("Cannot place mock call");
+ }
+
+
+ Collection lastCall = callHistoryService.findLast(1);
+
+ assertEquals("There must be 1 Call", lastCall.size(), 1);
+
+ CallRecord callRecord = (CallRecord)lastCall.iterator().next();
+
+ assertEquals("There must be 2 participants in the call",
+ callRecord.getParticipantRecords().size(), 2);
+
+ CallParticipantRecord callP1 =
+ callRecord.findParticipantRecord((String)participantAddresses.get(0));
+ CallParticipantRecord callP2 =
+ callRecord.findParticipantRecord((String)participantAddresses.get(1));
+
+ assertTrue("Second participant added after first one",
+ callP2.getStartTime().after(callP1.getStartTime()));
+
+ assertTrue("Second participant hanguped before first one",
+ callP2.getEndTime().before(callP1.getEndTime()));
+ }
+
+ private void dumpResult(Collection c)
+ {
+ Iterator rs = c.iterator();
+ while (rs.hasNext())
+ {
+ CallRecord hr = (CallRecord)rs.next();
+ logger.info("----------------------");
+ logger.info(hr.getParticipantRecords());
+ logger.info("----------------------");
+ }
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/callhistory/callhistory.slick.manifest.mf b/test/net/java/sip/communicator/slick/callhistory/callhistory.slick.manifest.mf
new file mode 100755
index 0000000..c1fafe9
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/callhistory/callhistory.slick.manifest.mf
@@ -0,0 +1,25 @@
+Bundle-Activator: net.java.sip.communicator.slick.callhistory.CallHistoryServiceLick
+Bundle-Name: Call History Service Implementation Compatibility Kit
+Bundle-Description: A Service Implementation Compatibility Kit for the History Service
+Bundle-Vendor: sip-communicator.org
+Bundle-Version: 0.0.1
+Import-Package: junit.framework,
+ net.java.sip.communicator.slick.history,
+ net.java.sip.communicator.service.history.records,
+ net.java.sip.communicator.service.history,
+ net.java.sip.communicator.service.contactlist,
+ net.java.sip.communicator.service.callhistory,
+ net.java.sip.communicator.service.callhistory.event,
+ net.java.sip.communicator.impl.protocol.mock,
+ net.java.sip.communicator.service.protocol,
+ net.java.sip.communicator.service.protocol.event,
+ org.osgi.framework,
+ org.w3c.dom,
+ javax.xml.parsers,
+ net.java.sip.communicator.util,
+ net.java.sip.communicator.util.xml,
+ javax.xml.transform,
+ javax.xml.transform.dom,
+ javax.xml.transform.stream,
+ org.apache.xml.serializer,
+Export-Package: net.java.sip.communicator.slick.callhistory,