/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.java.sip.communicator.impl.callhistory; import java.util.*; import net.java.sip.communicator.service.callhistory.*; import net.java.sip.communicator.service.contactsource.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.util.*; /** * The <tt>CallHistorySourceContact</tt> is an implementation of the * <tt>SourceContact</tt> interface based on a <tt>CallRecord</tt>. * * @author Yana Stamcheva */ public class CallHistorySourceContact extends DataObject implements SourceContact { /** * Whether we need to strip saved addresses to numbers. We strip everything * before '@', if it is absent nothing is changed from the saved address. */ private static final String STRIP_ADDRESSES_TO_NUMBERS = "net.java.sip.communicator.impl.callhistory.STRIP_ADDRESSES_TO_NUMBERS"; /** * The parent <tt>CallHistoryContactSource</tt>, where this contact is * contained. */ private final CallHistoryContactSource contactSource; /** * The corresponding call record. */ private final CallRecord callRecord; /** * The incoming call icon. */ private static final byte[] incomingIcon = CallHistoryActivator.getResources() .getImageInBytes("service.gui.icons.INCOMING_CALL"); /** * The outgoing call icon. */ private static byte[] outgoingIcon = CallHistoryActivator.getResources() .getImageInBytes("service.gui.icons.OUTGOING_CALL"); /** * The missed call icon. */ private static byte[] missedCallIcon = CallHistoryActivator.getResources() .getImageInBytes("service.gui.icons.MISSED_CALL"); /** * A list of all contact details. */ private final List<ContactDetail> contactDetails = new LinkedList<ContactDetail>(); /** * The display name of this contact. */ private String displayName = ""; /** * The display details of this contact. */ private final String displayDetails; /** * Creates an instance of <tt>CallHistorySourceContact</tt> * @param contactSource the contact source * @param callRecord the call record */ public CallHistorySourceContact(CallHistoryContactSource contactSource, CallRecord callRecord) { this.contactSource = contactSource; this.callRecord = callRecord; this.initPeerDetails(); this.displayDetails = CallHistoryActivator.getResources() .getI18NString("service.gui.AT") + ": " + getDateString(callRecord.getStartTime().getTime()) + " " + CallHistoryActivator.getResources() .getI18NString("service.gui.DURATION") + ": " + GuiUtils.formatTime( callRecord.getStartTime(), callRecord.getEndTime()); } /** * Initializes peer details. */ private void initPeerDetails() { boolean stripAddress = false; String stripAddressProp = CallHistoryActivator.getResources() .getSettingsString(STRIP_ADDRESSES_TO_NUMBERS); if(stripAddressProp != null && Boolean.parseBoolean(stripAddressProp)) stripAddress = true; Iterator<CallPeerRecord> recordsIter = callRecord.getPeerRecords().iterator(); while (recordsIter.hasNext()) { CallPeerRecord peerRecord = recordsIter.next(); String peerAddress = peerRecord.getPeerAddress(); String peerSecondaryAddress = peerRecord.getPeerSecondaryAddress(); if (peerAddress != null) { if(stripAddress && !peerAddress.startsWith("@")) { peerAddress = peerAddress.split("@")[0]; } String peerRecordDisplayName = peerRecord.getDisplayName(); if(peerRecordDisplayName == null || peerRecordDisplayName.length() == 0) peerRecordDisplayName = peerAddress; ContactDetail contactDetail = new ContactDetail(peerAddress, peerRecordDisplayName); Map<Class<? extends OperationSet>, ProtocolProviderService> preferredProviders = null; Map<Class<? extends OperationSet>, String> preferredProtocols = null; ProtocolProviderService preferredProvider = callRecord.getProtocolProvider(); if (preferredProvider != null) { preferredProviders = new Hashtable<Class<? extends OperationSet>, ProtocolProviderService>(); OperationSetPresence opSetPres = preferredProvider.getOperationSet( OperationSetPresence.class); Contact contact = null; if(opSetPres != null) contact = opSetPres.findContactByID(peerAddress); OperationSetContactCapabilities opSetCaps = preferredProvider.getOperationSet( OperationSetContactCapabilities.class); if(opSetCaps != null && opSetPres != null) { if(contact != null && opSetCaps.getOperationSet( contact, OperationSetBasicTelephony.class) != null) { preferredProviders.put( OperationSetBasicTelephony.class, preferredProvider); } } else { preferredProviders.put(OperationSetBasicTelephony.class, preferredProvider); } contactDetail.setPreferredProviders(preferredProviders); } // If there's no preferred provider set we just specify that // the SIP protocol should be used for the telephony operation // set. This is needed for all history records stored before // the protocol provider property had been introduced. else { preferredProtocols = new Hashtable<Class<? extends OperationSet>, String>(); preferredProtocols.put( OperationSetBasicTelephony.class, ProtocolNames.SIP); contactDetail.setPreferredProtocols(preferredProtocols); } LinkedList<Class<? extends OperationSet>> supportedOpSets = new LinkedList<Class<? extends OperationSet>>(); // if the contat supports call if((preferredProviders != null && preferredProviders.containsKey( OperationSetBasicTelephony.class)) || (preferredProtocols != null)) { supportedOpSets.add(OperationSetBasicTelephony.class); } // can be added as contacts supportedOpSets.add(OperationSetPersistentPresence.class); contactDetail.setSupportedOpSets(supportedOpSets); contactDetails.add(contactDetail); if(peerSecondaryAddress != null) { ContactDetail secondaryContactDetail = new ContactDetail(peerSecondaryAddress); secondaryContactDetail.addSupportedOpSet( OperationSetPersistentPresence.class); contactDetails.add(secondaryContactDetail); } // Set the displayName. String name = peerRecord.getDisplayName(); if (name == null || name.length() <= 0) name = peerAddress; if (displayName == null || displayName.length() <= 0) if (callRecord.getPeerRecords().size() > 1) displayName = "Conference " + name; else displayName = name; } } } /** * Returns a list of available contact details. * @return a list of available contact details */ public List<ContactDetail> getContactDetails() { return new LinkedList<ContactDetail>(contactDetails); } /** * Returns the parent <tt>ContactSourceService</tt> from which this contact * came from. * @return the parent <tt>ContactSourceService</tt> from which this contact * came from */ public ContactSourceService getContactSource() { return contactSource; } /** * Returns the display details of this search contact. This could be any * important information that should be shown to the user. * * @return the display details of the search contact */ public String getDisplayDetails() { return displayDetails; } /** * Returns the display name of this search contact. This is a user-friendly * name that could be shown in the user interface. * * @return the display name of this search contact */ public String getDisplayName() { return displayName; } /** * An image (or avatar) corresponding to this search contact. If such is * not available this method will return null. * * @return the byte array of the image or null if no image is available */ public byte[] getImage() { if (callRecord.getDirection().equals(CallRecord.IN)) { // if the call record has reason for normal call clearing // means it was answered somewhere else and we don't // mark it as missed if (callRecord.getStartTime().equals(callRecord.getEndTime()) && (callRecord.getEndReason() != CallPeerChangeEvent.NORMAL_CALL_CLEARING)) return missedCallIcon; else return incomingIcon; } else if (callRecord.getDirection().equals(CallRecord.OUT)) return outgoingIcon; return null; } /** * Returns a list of all <tt>ContactDetail</tt>s supporting the given * <tt>OperationSet</tt> class. * @param operationSet the <tt>OperationSet</tt> class we're looking for * @return a list of all <tt>ContactDetail</tt>s supporting the given * <tt>OperationSet</tt> class */ public List<ContactDetail> getContactDetails( Class<? extends OperationSet> operationSet) { // We support only call details // or persistence presence so we can add contacts. if (!(operationSet.equals(OperationSetBasicTelephony.class) || operationSet.equals(OperationSetPersistentPresence.class))) return null; return new LinkedList<ContactDetail>(contactDetails); } /** * Returns a list of all <tt>ContactDetail</tt>s corresponding to the given * category. * @param category the <tt>OperationSet</tt> class we're looking for * @return a list of all <tt>ContactDetail</tt>s corresponding to the given * category */ public List<ContactDetail> getContactDetails( ContactDetail.Category category) throws OperationNotSupportedException { // We don't support category for call history details, so we return null. throw new OperationNotSupportedException( "Categories are not supported for call history records."); } /** * Returns the preferred <tt>ContactDetail</tt> for a given * <tt>OperationSet</tt> class. * @param operationSet the <tt>OperationSet</tt> class, for which we would * like to obtain a <tt>ContactDetail</tt> * @return the preferred <tt>ContactDetail</tt> for a given * <tt>OperationSet</tt> class */ public ContactDetail getPreferredContactDetail( Class<? extends OperationSet> operationSet) { // We support only call details // or persistence presence so we can add contacts. if (!(operationSet.equals(OperationSetBasicTelephony.class) || operationSet.equals(OperationSetPersistentPresence.class))) return null; return contactDetails.get(0); } /** * Returns the date string to show for the given date. * * @param date the date to format * @return the date string to show for the given date */ public static String getDateString(long date) { String time = GuiUtils.formatTime(date); // If the current date we don't go in there and we'll return just the // time. if (GuiUtils.compareDatesOnly(date, System.currentTimeMillis()) < 0) { StringBuffer dateStrBuf = new StringBuffer(); GuiUtils.formatDate(date, dateStrBuf); dateStrBuf.append(" "); dateStrBuf.append(time); return dateStrBuf.toString(); } return time; } /** * Returns the status of the source contact. And null if such information * is not available. * @return the PresenceStatus representing the state of this source contact. */ public PresenceStatus getPresenceStatus() { return null; } /** * Returns the index of this source contact in its parent. * * @return the index of this source contact in its parent */ public int getIndex() { return -1; } /** * {@inheritDoc} * * Not implemented. */ @Override public String getContactAddress() { return null; } /** * {@inheritDoc} * * Not implemented. */ @Override public void setContactAddress(String contactAddress) { } /** * Whether the current image returned by @see #getImage() is the one * provided by the SourceContact by default, or is a one used and obtained * from external source. * * @return whether this is the default image for this SourceContact. */ @Override public boolean isDefaultImage() { // in this SourceContact we always show a default image based // on the call direction (in, out or missed) return true; } }