/*
* Jitsi, 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.globaldisplaydetails;
import java.util.*;
import net.java.sip.communicator.service.globaldisplaydetails.*;
import net.java.sip.communicator.service.globaldisplaydetails.event.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.account.*;
import org.jitsi.util.*;
/**
* The GlobalDisplayNameImpl offers generic access to a global
* display name for the local user.
*
*
* @author Yana Stamcheva
* @author Hristo Terezov
*/
public class GlobalDisplayDetailsImpl
implements GlobalDisplayDetailsService,
RegistrationStateChangeListener,
ServerStoredDetailsChangeListener,
AvatarListener
{
/**
* Property to disable auto answer menu.
*/
private static final String GLOBAL_DISPLAY_NAME_PROP =
"net.java.sip.communicator.impl.gui.main.presence.GLOBAL_DISPLAY_NAME";
/**
* The display details listeners list.
*/
private List displayDetailsListeners
= new ArrayList();
/**
* The current first name.
*/
private String currentFirstName;
/**
* The current last name.
*/
private String currentLastName;
/**
* The current display name.
*/
private String currentDisplayName;
/**
* The provisioned display name.
*/
private String provisionedDisplayName;
/**
* The global avatar.
*/
private static byte[] globalAvatar;
/**
* The global display name.
*/
private String globalDisplayName;
/**
* Creates an instance of GlobalDisplayDetailsImpl.
*/
public GlobalDisplayDetailsImpl()
{
provisionedDisplayName
= GlobalDisplayDetailsActivator.getConfigurationService()
.getString(GLOBAL_DISPLAY_NAME_PROP, null);
Iterator providersIter
= AccountUtils.getRegisteredProviders().iterator();
while (providersIter.hasNext())
providersIter.next().addRegistrationStateChangeListener(this);
}
/**
* Returns default display name for the given provider or the global display
* name.
* @param pps the given protocol provider service
* @return default display name.
*/
public String getDisplayName(ProtocolProviderService pps)
{
final OperationSetServerStoredAccountInfo accountInfoOpSet
= pps.getOperationSet(
OperationSetServerStoredAccountInfo.class);
String displayName = "";
if (accountInfoOpSet != null)
{
displayName = AccountInfoUtils.getDisplayName(accountInfoOpSet);
}
if(displayName == null || displayName.length() == 0)
{
displayName = getGlobalDisplayName();
if(displayName == null || displayName.length() == 0)
{
displayName = pps.getAccountID().getUserID();
if(displayName != null)
{
int atIndex = displayName.lastIndexOf("@");
if (atIndex > 0)
displayName = displayName.substring(0, atIndex);
}
}
}
return displayName;
}
/**
* Returns the global display name to be used to identify the local user.
*
* @return a string representing the global local user display name
*/
public String getGlobalDisplayName()
{
if (!StringUtils.isNullOrEmpty(provisionedDisplayName))
return provisionedDisplayName;
return globalDisplayName;
}
/**
* Sets the global local user display name.
*
* @param displayName the string representing the display name to set as
* a global display name
*/
public void setGlobalDisplayName(String displayName)
{
globalDisplayName = displayName;
}
/**
* Returns the global avatar for the local user.
*
* @return a byte array containing the global avatar for the local user
*/
public byte[] getGlobalDisplayAvatar()
{
return globalAvatar;
}
/**
* Sets the global display avatar for the local user.
*
* @param avatar the byte array representing the avatar to set
*/
public void setGlobalDisplayAvatar(byte[] avatar)
{
globalAvatar = avatar;
}
/**
* Adds the given GlobalDisplayDetailsListener to listen for change
* events concerning the global display details.
*
* @param l the GlobalDisplayDetailsListener to add
*/
public void addGlobalDisplayDetailsListener(GlobalDisplayDetailsListener l)
{
synchronized (displayDetailsListeners)
{
if (!displayDetailsListeners.contains(l))
displayDetailsListeners.add(l);
}
}
/**
* Removes the given GlobalDisplayDetailsListener listening for
* change events concerning the global display details.
*
* @param l the GlobalDisplayDetailsListener to remove
*/
public void removeGlobalDisplayDetailsListener(
GlobalDisplayDetailsListener l)
{
synchronized (displayDetailsListeners)
{
if (displayDetailsListeners.contains(l))
displayDetailsListeners.remove(l);
}
}
/**
* Updates account information when a protocol provider is registered.
* @param evt the RegistrationStateChangeEvent that notified us
* of the change
*/
public void registrationStateChanged(RegistrationStateChangeEvent evt)
{
ProtocolProviderService protocolProvider = evt.getProvider();
if (evt.getNewState().equals(RegistrationState.REGISTERED))
{
/*
* Check the support for OperationSetServerStoredAccountInfo prior
* to starting the Thread because only a couple of the protocols
* currently support it and thus starting a Thread that is not going
* to do anything useful can be prevented.
*/
OperationSetServerStoredAccountInfo accountInfoOpSet
= protocolProvider.getOperationSet(
OperationSetServerStoredAccountInfo.class);
if (accountInfoOpSet != null)
{
/*
* FIXME Starting a separate Thread for each
* ProtocolProviderService is uncontrollable because the
* application is multi-protocol and having multiple accounts is
* expected so one is likely to end up with a multitude of
* Threads. Besides, it not very clear when retrieving the first
* and last name is to stop so one ProtocolProviderService being
* able to supply both the first and the last name may be
* overwritten by a ProtocolProviderService which is able to
* provide just one of them.
*/
new UpdateAccountInfo(protocolProvider, accountInfoOpSet, false)
.start();
}
OperationSetAvatar avatarOpSet
= protocolProvider.getOperationSet(OperationSetAvatar.class);
if (avatarOpSet != null)
avatarOpSet.addAvatarListener(this);
OperationSetServerStoredAccountInfo serverStoredAccountInfo
= protocolProvider.getOperationSet(
OperationSetServerStoredAccountInfo.class);
if (serverStoredAccountInfo != null)
serverStoredAccountInfo.addServerStoredDetailsChangeListener(
this);
}
else if (evt.getNewState().equals(RegistrationState.UNREGISTERING)
|| evt.getNewState().equals(RegistrationState.CONNECTION_FAILED))
{
OperationSetAvatar avatarOpSet
= protocolProvider.getOperationSet(OperationSetAvatar.class);
if (avatarOpSet != null)
avatarOpSet.removeAvatarListener(this);
OperationSetServerStoredAccountInfo serverStoredAccountInfo
= protocolProvider.getOperationSet(
OperationSetServerStoredAccountInfo.class);
if (serverStoredAccountInfo != null)
serverStoredAccountInfo.removeServerStoredDetailsChangeListener(
this);
}
}
/**
* Called whenever a new avatar is defined for one of the protocols that we
* have subscribed for.
*
* @param event the event containing the new image
*/
public void avatarChanged(AvatarEvent event)
{
globalAvatar = event.getNewAvatar();
// If there is no avatar image set, then displays the default one.
if(globalAvatar == null)
{
globalAvatar = GlobalDisplayDetailsActivator.getResources()
.getImageInBytes("service.gui.DEFAULT_USER_PHOTO");
}
AvatarCacheUtils.cacheAvatar(
event.getSourceProvider(), globalAvatar);
fireGlobalAvatarEvent(globalAvatar);
}
/**
* Registers a ServerStoredDetailsChangeListener with the operation sets
* of the providers, if a provider change its name we use it in the UI.
*
* @param evt the ServerStoredDetailsChangeEvent
* the event for name change.
*/
public void serverStoredDetailsChanged(ServerStoredDetailsChangeEvent evt)
{
if(!StringUtils.isNullOrEmpty(provisionedDisplayName))
return;
if(evt.getNewValue() instanceof
ServerStoredDetails.DisplayNameDetail
&& (evt.getEventID() == ServerStoredDetailsChangeEvent.DETAIL_ADDED
|| evt.getEventID()
== ServerStoredDetailsChangeEvent.DETAIL_REPLACED))
{
ProtocolProviderService protocolProvider = evt.getProvider();
OperationSetServerStoredAccountInfo accountInfoOpSet
= protocolProvider.getOperationSet(
OperationSetServerStoredAccountInfo.class);
new UpdateAccountInfo( evt.getProvider(),
accountInfoOpSet,
true).start();
}
}
/**
* Queries the operations sets to obtain names and display info.
* Queries are done in separate thread.
*/
private class UpdateAccountInfo
extends Thread
{
/**
* The protocol provider.
*/
private ProtocolProviderService protocolProvider;
/**
* The account info operation set to query.
*/
private OperationSetServerStoredAccountInfo accountInfoOpSet;
/**
* Indicates if the display name and avatar should be updated from this
* provider even if they already have values.
*/
private boolean isUpdate;
/**
* Constructs with provider and opset to use.
* @param protocolProvider the provider.
* @param accountInfoOpSet the opset.
* @param isUpdate indicates if the display name and avatar should be
* updated from this provider even if they already have values.
*/
UpdateAccountInfo(
ProtocolProviderService protocolProvider,
OperationSetServerStoredAccountInfo accountInfoOpSet,
boolean isUpdate)
{
this.protocolProvider = protocolProvider;
this.accountInfoOpSet = accountInfoOpSet;
}
@Override
public void run()
{
if (globalAvatar == null)
{
globalAvatar
= AvatarCacheUtils
.getCachedAvatar(protocolProvider);
if (globalAvatar == null)
{
byte[] accountImage
= AccountInfoUtils
.getImage(accountInfoOpSet);
// do not set empty images
if ((accountImage != null)
&& (accountImage.length > 0))
{
globalAvatar = accountImage;
AvatarCacheUtils.cacheAvatar(
protocolProvider, accountImage);
}
}
if (globalAvatar != null && globalAvatar.length > 0)
{
fireGlobalAvatarEvent(globalAvatar);
}
}
if(!StringUtils.isNullOrEmpty(provisionedDisplayName)
|| (!StringUtils.isNullOrEmpty(globalDisplayName) && !isUpdate))
return;
if (currentFirstName == null)
{
String firstName = AccountInfoUtils
.getFirstName(accountInfoOpSet);
if (!StringUtils.isNullOrEmpty(firstName))
{
currentFirstName = firstName;
}
}
if (currentLastName == null)
{
String lastName = AccountInfoUtils
.getLastName(accountInfoOpSet);
if (!StringUtils.isNullOrEmpty(lastName))
{
currentLastName = lastName;
}
}
if (currentFirstName == null && currentLastName == null)
{
String displayName = AccountInfoUtils
.getDisplayName(accountInfoOpSet);
if (displayName != null)
currentDisplayName = displayName;
}
setGlobalDisplayName();
}
/**
* Called on the event dispatching thread (not on the worker thread)
* after the construct
method has returned.
*/
protected void setGlobalDisplayName()
{
String accountName = null;
if (!StringUtils.isNullOrEmpty(currentFirstName))
{
accountName = currentFirstName;
}
if (!StringUtils.isNullOrEmpty(currentLastName))
{
/*
* If accountName is null, don't use += because
* it will make the accountName start with the
* string "null".
*/
if (StringUtils.isNullOrEmpty(accountName))
accountName = currentLastName;
else
accountName += " " + currentLastName;
}
if (currentFirstName == null && currentLastName == null)
{
if (currentDisplayName != null)
accountName = currentDisplayName;
}
globalDisplayName = accountName;
if (!StringUtils.isNullOrEmpty(globalDisplayName))
{
fireGlobalDisplayNameEvent(globalDisplayName);
}
}
}
/**
* Notifies all interested listeners of a global display details change.
*
* @param displayName the new display name
*/
private void fireGlobalDisplayNameEvent(String displayName)
{
List listeners;
synchronized (displayDetailsListeners)
{
listeners = Collections.unmodifiableList(displayDetailsListeners);
}
Iterator listIter
= listeners.iterator();
while (listIter.hasNext())
{
listIter.next().globalDisplayNameChanged(
new GlobalDisplayNameChangeEvent(this, displayName));
}
}
/**
* Notifies all interested listeners of a global display details change.
*
* @param avatar the new avatar
*/
private void fireGlobalAvatarEvent(byte[] avatar)
{
List listeners;
synchronized (displayDetailsListeners)
{
listeners = Collections.unmodifiableList(displayDetailsListeners);
}
Iterator listIter
= listeners.iterator();
while (listIter.hasNext())
{
listIter.next().globalDisplayAvatarChanged(
new GlobalAvatarChangeEvent(this, avatar));
}
}
}