Adds the possibility to edit account details. Patch provided by Marin

Dzhigarov on dev (Nov 8, 2013).
cusax-fix 4905
yanas 13 years ago
parent fa69d4d2e7
commit d55b3efebe

@ -83,5 +83,6 @@
<classpathentry kind="lib" path="lib/installer-exclude/bcprov-jdk15on-149.jar"/>
<classpathentry kind="lib" path="lib/installer-exclude/bccontrib-1.0-SNAPSHOT.jar"/>
<classpathentry kind="lib" path="lib/installer-exclude/zrtp4j-light.jar"/>
<classpathentry kind="lib" path="lib/installer-exclude/jcalendar-1.4.jar"/>
<classpathentry kind="output" path="classes"/>
</classpath>

@ -1079,8 +1079,8 @@
bundle-globalshortcut,bundle-plugin-msofficecomm,bundle-libjitsi,
bundle-customcontactactions, bundle-phonenumbercontactsource,
bundle-demuxcontactsource, bundle-muc,
bundle-desktoputil,
bundle-globaldisplaydetails,bundle-plugin-propertieseditor"/>
bundle-desktoputil,bundle-globaldisplaydetails,
bundle-plugin-propertieseditor,bundle-plugin-accountinfo"/>
<!--BUNDLE-SC-LAUNCHER-->
<target name="bundle-sc-launcher">
@ -2226,6 +2226,7 @@ javax.swing.event, javax.swing.border"/>
manifest="${src}/net/java/sip/communicator/plugin/accountinfo/accountinfo.manifest.mf">
<zipfileset dir="${dest}/net/java/sip/communicator/plugin/accountinfo"
prefix="net/java/sip/communicator/plugin/accountinfo"/>
<zipfileset src="${lib.noinst}/jcalendar-1.4.jar"/>
</jar>
</target>

@ -134,6 +134,7 @@ felix.auto.start.60= \
felix.auto.start.66= \
reference:file:sc-bundles/swing-ui.jar \
reference:file:sc-bundles/update.jar \
reference:file:sc-bundles/accountinfo.jar \
reference:file:sc-bundles/swingnotification.jar \
reference:file:sc-bundles/systray-service.jar \
reference:file:sc-bundles/osdependent.jar \

@ -123,6 +123,7 @@ service.gui.statusicons.USER_FFC_ICON=resources/images/impl/gui/common/statusico
service.gui.statusicons.USER_ON_THE_PHONE_ICON=resources/images/impl/gui/common/statusicons/onThePhone.png
# service gui buttons
service.gui.buttons.ACCOUNT_EDIT_ICON=resources/images/impl/gui/buttons/accountEditIcon.png
service.gui.buttons.CONTACT_LIST_BUTTON_BG_LEFT=resources/images/impl/gui/buttons/contactListButtonBgLeft.png
service.gui.buttons.CONTACT_LIST_BUTTON_BG_RIGHT=resources/images/impl/gui/buttons/contactListButtonBgRight.png
service.gui.buttons.CONTACT_LIST_BUTTON_BG_MIDDLE=resources/images/impl/gui/buttons/contactListButtonBgMiddle.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -857,15 +857,33 @@ impl.googlecontacts.WRONG_CREDENTIALS=Wrong credentials for Google account {0}
plugin.accountinfo.TITLE=Account Info
plugin.accountinfo.EXTENDED=Extended
plugin.accountinfo.NOT_SUPPORTED=Account info not available.
plugin.accountinfo.SELECT_ACCOUNT=Please select an account:
plugin.accountinfo.DISPLAY_NAME=Display name:
plugin.accountinfo.FIRST_NAME=First Name:
plugin.accountinfo.MIDDLE_NAME=Middle Name:
plugin.accountinfo.LAST_NAME=Last Name:
plugin.accountinfo.NICKNAME=Nickname:
plugin.accountinfo.URL=URL:
plugin.accountinfo.AGE=Age:
plugin.accountinfo.BDAY=Birth Date:
plugin.accountinfo.BDAY_FORMAT=MMM dd, yyyy
plugin.accountinfo.GENDER=Gender:
plugin.accountinfo.STREET=Street Address:
plugin.accountinfo.CITY=City:
plugin.accountinfo.REGION=Region:
plugin.accountinfo.POST=Postal code:
plugin.accountinfo.COUNTRY=Country:
plugin.accountinfo.EMAIL=E-mail:
plugin.accountinfo.WORK_EMAIL=Work E-mail:
plugin.accountinfo.PHONE=Phone:
plugin.accountinfo.WORK_PHONE=Work Phone:
plugin.accountinfo.ORGANIZATION=Organization Name:
plugin.accountinfo.JOB_TITLE=Job Title:
plugin.accountinfo.ABOUT_ME=About Me:
plugin.accountinfo.ABOUT_ME_MAX_CHARACTERS=200
plugin.accountinfo.USER_PICTURES=User Pictures
plugin.accountinfo.GLOBAL_ICON=Use global icon
plugin.accountinfo.LOCAL_ICON=Use this icon:
plugin.accountinfo.CHANGE=Change
plugin.accountinfo.ONLY_MESSAGE=Only messages

@ -186,7 +186,7 @@ public MainFrame()
this.contactListPanel = new ContactListPane(this);
this.accountStatusPanel = new AccountStatusPanel(this);
this.accountStatusPanel = new AccountStatusPanel();
this.searchField = new SearchField( this,
TreeContactList.searchFilter,

@ -56,6 +56,12 @@ public class AccountList
*/
private final JButton editButton;
/**
* The menu that appears when right click on account is detected.
*/
private final AccountRightButtonMenu rightButtonMenu
= new AccountRightButtonMenu();
/**
* Creates an instance of this account list by specifying the parent
* container of the list.
@ -69,6 +75,29 @@ public AccountList(AccountsConfigurationPanel parentConfigPanel)
this.addMouseListener(this);
this.addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
if (SwingUtilities.isRightMouseButton(e))
{
Point point = e.getPoint();
AccountList.this.setSelectedIndex(getRow(point));
rightButtonMenu.setAccount(getSelectedAccount());
SwingUtilities.convertPointToScreen(
point, AccountList.this);
((JPopupMenu) rightButtonMenu).setInvoker(AccountList.this);
rightButtonMenu.setLocation(point.x, point.y);
rightButtonMenu.setVisible(true);
}
}
});
this.accountsInit();
GuiActivator.bundleContext.addServiceListener(this);
@ -76,6 +105,11 @@ public AccountList(AccountsConfigurationPanel parentConfigPanel)
this.editButton = parentConfigPanel.getEditButton();
}
private int getRow(Point point)
{
return locationToIndex(point);
}
/**
* Initializes the accounts table.
*/

@ -0,0 +1,260 @@
/*
* 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.gui.main.account;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.event.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.gui.Container;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.skin.*;
import org.jitsi.service.resources.*;
import org.osgi.framework.*;
/**
* AccountRightButtonMenu is the menu that opens when user right clicks on any
* of his accounts in the account list section.
*
* @author Marin Dzhigarov
*/
public class AccountRightButtonMenu
extends SIPCommPopupMenu
implements ActionListener,
PluginComponentListener,
Skinnable
{
/**
* The serial version UID.
*/
private static final long serialVersionUID = 1L;
/**
* The logger of this class.
*/
private final Logger logger
= Logger.getLogger(AccountRightButtonMenu.class);
/**
* The Account that is clicked on.
*/
private Account account = null;
/**
* A list of all PluginComponents that are registered through the OSGi
* bundle context.
*/
private java.util.List<PluginComponent> pluginComponents
= new ArrayList<PluginComponent>();
/**
* The edit item.
*/
private final JMenuItem editItem = new JMenuItem(
GuiActivator.getResources().getI18NString(
"service.gui.EDIT"));
/**
* Creates an instance of AccountRightButtonMenu
*/
public AccountRightButtonMenu()
{
super();
this.setLocation(getLocation());
this.init();
loadSkin();
}
/**
* Sets the current Account that is clicked on.
* @param account the Account that is clicked on.
*/
public void setAccount(Account account)
{
this.account = account;
editItem.setEnabled(account != null && this.account.isEnabled());
for (PluginComponent pluginComponent : pluginComponents)
pluginComponent.setCurrentAccountID(account.getAccountID());
}
/**
* Returns the Account that was last clicked on.
* @return the Account that was last clicked on.
*/
public Account getAccount()
{
return account;
}
/**
* Initialized the menu by adding all containing menu items.
*/
private void init()
{
initPluginComponents();
add(editItem);
editItem.addActionListener(this);
}
/**
* Initializes plug-in components for this container.
*/
private void initPluginComponents()
{
// Search for plugin components registered through the OSGI bundle
// context.
ServiceReference[] serRefs = null;
String osgiFilter = "("
+ Container.CONTAINER_ID
+ "="+Container.CONTAINER_ACCOUNT_RIGHT_BUTTON_MENU.getID()+")";
try
{
serRefs = GuiActivator.bundleContext.getServiceReferences(
PluginComponentFactory.class.getName(),
osgiFilter);
}
catch (InvalidSyntaxException exc)
{
logger.error("Could not obtain plugin reference.", exc);
}
if (serRefs != null)
{
for (int i = 0; i < serRefs.length; i ++)
{
PluginComponentFactory factory =
(PluginComponentFactory) GuiActivator
.bundleContext.getService(serRefs[i]);
PluginComponent component =
factory.getPluginComponentInstance(this);
if (component.getComponent() == null)
continue;
pluginComponents.add(component);
if (factory.getPositionIndex() != -1)
this.add((Component)component.getComponent(),
factory.getPositionIndex());
else
this.add((Component)component.getComponent());
}
}
GuiActivator.getUIService().addPluginComponentListener(this);
}
/**
* Reloads skin related information.
*/
public void loadSkin()
{
editItem.setIcon(new ImageIcon(
ImageLoader.getImage(ImageLoader.ACCOUNT_EDIT_ICON)));
}
/**
* Indicates that a plugin component has been added to this container.
*
* @param event the <tt>PluginComponentEvent</tt> that notified us
*/
/**
* Indicates that a new plugin component has been added. Adds it to this
* container if it belongs to it.
*
* @param event the <tt>PluginComponentEvent</tt> that notified us
*/
public void pluginComponentAdded(PluginComponentEvent event)
{
PluginComponentFactory factory = event.getPluginComponentFactory();
if (!factory.getContainer().equals(
Container.CONTAINER_ACCOUNT_RIGHT_BUTTON_MENU))
return;
PluginComponent c = factory.getPluginComponentInstance(this);
this.add((Component) c.getComponent());
this.repaint();
}
/**
* Removes the according plug-in component from this container.
* @param event the <tt>PluginComponentEvent</tt> that notified us
*/
public void pluginComponentRemoved(PluginComponentEvent event)
{
PluginComponentFactory factory = event.getPluginComponentFactory();
if(factory.getContainer()
.equals(Container.CONTAINER_ACCOUNT_RIGHT_BUTTON_MENU))
{
Component c =
(Component)factory.getPluginComponentInstance(this)
.getComponent();
this.remove(c);
pluginComponents.remove(c);
}
}
/**
* Handles the <tt>ActionEvent</tt>. Determines which menu item was
* selected and performs the appropriate operations.
* @param e the <tt>ActionEvent</tt>, which notified us of the action
*/
public void actionPerformed(ActionEvent e)
{
JMenuItem menuItem = (JMenuItem) e.getSource();
if (menuItem.equals(editItem))
{
if (account == null)
return;
AccountRegWizardContainerImpl wizard =
(AccountRegWizardContainerImpl) GuiActivator.getUIService()
.getAccountRegWizardContainer();
AccountRegistrationWizard protocolWizard =
wizard.getProtocolWizard(account.getProtocolProvider());
ResourceManagementService resources = GuiActivator.getResources();
if (protocolWizard != null)
{
wizard.setTitle(resources.getI18NString(
"service.gui.ACCOUNT_REGISTRATION_WIZARD"));
wizard.modifyAccount(account.getProtocolProvider());
wizard.showDialog(false);
}
else
{
// There is no wizard for this account - just show an error
// dialog:
String title = resources.getI18NString("service.gui.ERROR");
String message =
resources.getI18NString("service.gui.EDIT_NOT_SUPPORTED");
ErrorDialog dialog = new ErrorDialog(null, title, message);
dialog.setVisible(true);
}
}
}
}

@ -14,9 +14,9 @@
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.event.*;
import net.java.sip.communicator.impl.gui.lookandfeel.*;
import net.java.sip.communicator.impl.gui.main.*;
import net.java.sip.communicator.impl.gui.main.presence.avatar.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.plugin.desktoputil.presence.avatar.*;
import net.java.sip.communicator.service.globaldisplaydetails.event.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.gui.Container;
@ -25,8 +25,6 @@
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.skin.*;
import net.java.sip.communicator.plugin.desktoputil.*;
import org.jitsi.util.*;
/**
@ -126,15 +124,13 @@ public class AccountStatusPanel
/**
* Creates an instance of <tt>AccountStatusPanel</tt> by specifying the
* main window, where this panel is added.
* @param mainFrame the main window, where this panel is added
*/
public AccountStatusPanel(MainFrame mainFrame)
public AccountStatusPanel()
{
super(new BorderLayout(10, 0));
FramedImageWithMenu imageWithMenu
= new FramedImageWithMenu(
mainFrame,
new ImageIcon(
ImageLoader
.getImage(ImageLoader.DEFAULT_USER_PHOTO)),

@ -58,6 +58,7 @@ Import-Package: com.apple.eawt,
net.java.sip.communicator.plugin.desktoputil.event,
net.java.sip.communicator.plugin.desktoputil.plaf,
net.java.sip.communicator.plugin.desktoputil.presence,
net.java.sip.communicator.plugin.desktoputil.presence.avatar,
net.java.sip.communicator.plugin.desktoputil.transparent,
net.java.sip.communicator.service.customcontactactions,
net.java.sip.communicator.service.globaldisplaydetails,

@ -160,6 +160,12 @@ public class ImageLoader
public static final ImageID RIGHT_ARROW_ICON
= new ImageID("service.gui.icons.RIGHT_ARROW_ICON");
/**
* The edit icon that is shown when account is right clicked on.
*/
public static final ImageID ACCOUNT_EDIT_ICON
= new ImageID("service.gui.buttons.ACCOUNT_EDIT_ICON");
/**
* The call button image.
*/

@ -52,6 +52,7 @@ public class OperationSetServerStoredAccountInfoIcqImpl
public static final Map<Class<? extends GenericDetail>, int[]> supportedTypes
= new Hashtable<Class<? extends GenericDetail>, int[]>();
static {
supportedTypes.put(ServerStoredDetails.ImageDetail.class, new int[]{1});
supportedTypes.put(ServerStoredDetails.CountryDetail.class, new int[]{1, 0x01A4});
supportedTypes.put(ServerStoredDetails.NicknameDetail.class, new int[]{1, 0x0154});
supportedTypes.put(ServerStoredDetails.FirstNameDetail.class, new int[]{1, 0x0140});
@ -226,6 +227,23 @@ public boolean isDetailClassSupported(
return supportedTypes.get(detailClass) != null;
}
/**
* Determines whether the underlying implementation supports edition
* of this detail class.
* <p>
* @param detailClass the class whose edition we'd like to determine if it's
* possible
* @return true if the underlying implementation supports edition of this
* type of detail and false otherwise.
*/
public boolean isDetailClassEditable(
Class<? extends GenericDetail> detailClass)
{
return
isDetailClassSupported(detailClass)
&& ImageDetail.class.isAssignableFrom(detailClass);
}
/**
* Utility method throwing an exception if the icq stack is not properly
* initialized.
@ -794,6 +812,18 @@ else if(newDetailValue.equals(ServerStoredDetails.GenderDetail.MALE))
return false;
}
/*
* (non-Javadoc)
* @see net.java.sip.communicator.service.protocol.OperationSetServerStoredAccountInfo#save()
* This method is currently unimplemented.
* The idea behind this method is for users to call it only once, meaning
* that all ServerStoredDetails previously modified by addDetail/removeDetail
* and/or replaceDetail will be saved online on the server in one step.
* Currently, addDetail/removeDetail/replaceDetail methods are doing the
* actual saving but in the future the saving part must be carried here.
*/
public void save() throws OperationFailedException {}
/**
* Requests the account image if its missing.
* @return the new image or the one that has been already downloaded.

@ -8,6 +8,7 @@
import java.lang.reflect.*;
import java.net.*;
import java.text.*;
import java.util.*;
import net.java.sip.communicator.service.protocol.ServerStoredDetails.*;
@ -175,6 +176,24 @@ protected List<GenericDetail> retrieveDetails(String contactAddress)
if(tmp != null)
result.add(new NicknameDetail(tmp));
tmp = card.getField("BDAY");
if (tmp != null)
{
try
{
Calendar birthDateCalendar = Calendar.getInstance();
DateFormat dateFormat =
new SimpleDateFormat(
JabberActivator.getResources().getI18NString(
"plugin.accountinfo.BDAY_FORMAT"));
Date birthDate =
dateFormat.parse(tmp);
birthDateCalendar.setTime(birthDate);
BirthDateDetail bd = new BirthDateDetail(birthDateCalendar);
result.add(bd);
}
catch (ParseException e) {}
}
// Home Details
// addrField one of
// POSTAL, PARCEL, (DOM | INTL), PREF, POBOX, EXTADR, STREET,
@ -195,9 +214,9 @@ protected List<GenericDetail> retrieveDetails(String contactAddress)
if(tmp != null)
result.add(new PostalCodeDetail(tmp));
// tmp = card.getAddressFieldHome("CTRY");
// if(tmp != null)
// result.add(new CountryDetail(tmp);
tmp = card.getAddressFieldHome("CTRY");
if(tmp != null)
result.add(new CountryDetail(tmp));
// phoneType one of
//VOICE, FAX, PAGER, MSG, CELL, VIDEO, BBS, MODEM, ISDN, PCS, PREF
@ -276,7 +295,7 @@ protected List<GenericDetail> retrieveDetails(String contactAddress)
tmp = card.getEmailWork();
if(tmp != null)
result.add(new EmailAddressDetail(tmp));
result.add(new WorkEmailAddressDetail(tmp));
tmp = card.getOrganization();
if(tmp != null)
@ -286,15 +305,25 @@ protected List<GenericDetail> retrieveDetails(String contactAddress)
if(tmp != null)
result.add(new WorkDepartmentNameDetail(tmp));
tmp = card.getField("TITLE");
if(tmp != null)
result.add(new JobTitleDetail(tmp));
tmp = card.getField("ABOUTME");
if (tmp != null)
result.add(new AboutMeDetail(tmp));
byte[] imageBytes = card.getAvatar();
if(imageBytes != null && imageBytes.length > 0)
{
result.add(new ImageDetail("Image", imageBytes));
}
try
{
tmp = card.getField("URL");
if(tmp != null)
result.add(new WebPageDetail(new URL(tmp)));
result.add(new URLDetail("URL", new URL(tmp)));
}
catch(MalformedURLException e){}
}

@ -6,11 +6,12 @@
*/
package net.java.sip.communicator.impl.protocol.jabber;
import java.net.*;
import java.text.*;
import java.util.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.ServerStoredDetails.GenericDetail;
import net.java.sip.communicator.service.protocol.ServerStoredDetails.ImageDetail;
import net.java.sip.communicator.service.protocol.ServerStoredDetails.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
@ -22,6 +23,7 @@
* provider.
*
* @author Damian Minkov
* @author Marin Dzhigarov
*/
public class OperationSetServerStoredAccountInfoJabberImpl
extends AbstractOperationSetServerStoredAccountInfo
@ -42,6 +44,35 @@ public class OperationSetServerStoredAccountInfoJabberImpl
*/
private ProtocolProviderServiceJabberImpl jabberProvider = null;
/**
* List of all supported <tt>ServerStoredDetails</tt>
* for this implementation.
*/
public static final List<Class<? extends GenericDetail>> supportedTypes
= new ArrayList<Class<? extends GenericDetail>>();
static {
supportedTypes.add(ImageDetail.class);
supportedTypes.add(FirstNameDetail.class);
supportedTypes.add(MiddleNameDetail.class);
supportedTypes.add(LastNameDetail.class);
supportedTypes.add(NicknameDetail.class);
supportedTypes.add(AddressDetail.class);
supportedTypes.add(CityDetail.class);
supportedTypes.add(ProvinceDetail.class);
supportedTypes.add(PostalCodeDetail.class);
supportedTypes.add(CountryDetail.class);
supportedTypes.add(EmailAddressDetail.class);
supportedTypes.add(WorkEmailAddressDetail.class);
supportedTypes.add(PhoneNumberDetail.class);
supportedTypes.add(WorkPhoneDetail.class);
supportedTypes.add(WorkOrganizationNameDetail.class);
supportedTypes.add(URLDetail.class);
supportedTypes.add(BirthDateDetail.class);
supportedTypes.add(JobTitleDetail.class);
supportedTypes.add(AboutMeDetail.class);
}
/**
* Our account UIN.
*/
@ -124,14 +155,7 @@ public Iterator<GenericDetail> getAllAvailableDetails()
*/
public Iterator<Class<? extends GenericDetail>> getSupportedDetailTypes()
{
List<GenericDetail> details = infoRetreiver.getContactDetails(uin);
List<Class<? extends GenericDetail>> result
= new Vector<Class<? extends GenericDetail>>();
for (GenericDetail obj : details)
result.add(obj.getClass());
return result.iterator();
return supportedTypes.iterator();
}
/**
@ -150,12 +174,7 @@ public Iterator<Class<? extends GenericDetail>> getSupportedDetailTypes()
public boolean isDetailClassSupported(
Class<? extends GenericDetail> detailClass)
{
List<GenericDetail> details = infoRetreiver.getContactDetails(uin);
for (GenericDetail obj : details)
if(detailClass.isAssignableFrom(obj.getClass()))
return true;
return false;
return supportedTypes.contains(detailClass);
}
/**
@ -173,7 +192,7 @@ public int getMaxDetailInstances(Class<? extends GenericDetail> detailClass)
}
/**
* Adds the specified detail to the list of details registered on-line
* Adds the specified detail to the list of details ready to be saved online
* for this account. If such a detail already exists its max instance number
* is consulted and if it allows it - a second instance is added or otherwise
* and illegal argument exception is thrown. An IllegalArgumentException is
@ -187,67 +206,50 @@ public int getMaxDetailInstances(Class<? extends GenericDetail> detailClass)
* max instances number has been attained or if the underlying
* implementation does not support setting details of the corresponding
* class.
* @throws OperationFailedException with code Network Failure if putting the
* new value online has failed
* @throws java.lang.ArrayIndexOutOfBoundsException if the number of
* instances currently registered by the application is already equal to the
* maximum number of supported instances (@see getMaxDetailInstances())
*/
public void addDetail(ServerStoredDetails.GenericDetail detail)
throws IllegalArgumentException,
OperationFailedException,
ArrayIndexOutOfBoundsException
{
assertConnected();
/*
Currently as the function only provided the list of classes that
currently have data associated with them
in Jabber InfoRetreiver we have to skip this check*/
//if (!isDetailClassSupported(detail.getClass())) {
// throw new IllegalArgumentException(
// "implementation does not support such details " +
// detail.getClass());
//}
if (!isDetailClassSupported(detail.getClass())) {
throw new IllegalArgumentException(
"implementation does not support such details " +
detail.getClass());
}
Iterator<GenericDetail> iter = getDetails(detail.getClass());
int currentDetailsSize = 0;
while (iter.hasNext())
{
currentDetailsSize++;
iter.next();
}
if (currentDetailsSize >= getMaxDetailInstances(detail.getClass()))
if (currentDetailsSize > getMaxDetailInstances(detail.getClass()))
{
throw new ArrayIndexOutOfBoundsException(
"Max count for this detail is already reached");
}
if(detail instanceof ImageDetail)
{
// Push the avatar photo to the server.
this.uploadImageDetail(
ServerStoredDetailsChangeEvent.DETAIL_ADDED,
null,
detail);
}
infoRetreiver.getCachedContactDetails(uin).add(detail);
}
/**
* Removes the specified detail from the list of details stored online for
* this account. The method returns a boolean indicating if such a detail
* was found (and removed) or not.
* Removes the specified detail from the list of details ready to be saved
* online this account. The method returns a boolean indicating if such a
* detail was found (and removed) or not.
* <p>
* @param detail the detail to remove
* @return true if the specified detail existed and was successfully removed
* and false otherwise.
* @throws OperationFailedException with code Network Failure if removing the
* detail from the server has failed
*/
public boolean removeDetail(ServerStoredDetails.GenericDetail detail)
throws OperationFailedException
{
return false;
return infoRetreiver.getCachedContactDetails(uin).remove(detail);
}
/**
@ -264,16 +266,12 @@ public boolean removeDetail(ServerStoredDetails.GenericDetail detail)
* call to addDetail is required).
* @throws ClassCastException if newDetailValue is not an instance of the
* same class as currentDetailValue.
* @throws OperationFailedException with code Network Failure if putting the
* new value back online has failed
*/
public boolean replaceDetail(
ServerStoredDetails.GenericDetail currentDetailValue,
ServerStoredDetails.GenericDetail newDetailValue)
throws ClassCastException, OperationFailedException
throws ClassCastException
{
assertConnected();
if (!newDetailValue.getClass().equals(currentDetailValue.getClass()))
{
throw new ClassCastException(
@ -304,22 +302,130 @@ public boolean replaceDetail(
return false;
}
if(newDetailValue instanceof ImageDetail)
removeDetail(currentDetailValue);
addDetail(newDetailValue);
return true;
}
/**
* Saves the list of details for this account that were ready to be stored
* online on the server. This method performs the actual saving of details
* online on the server and is supposed to be invoked after addDetail(),
* replaceDetail() and/or removeDetail().
* <p>
* @throws OperationFailedException with code Network Failure if putting the
* new values back online has failed.
*/
public void save() throws OperationFailedException
{
assertConnected();
List<GenericDetail> details = infoRetreiver.getContactDetails(uin);
VCardXEP0153 vCard = new VCardXEP0153();
for (GenericDetail detail : details)
{
if (detail instanceof ImageDetail)
{
byte[] avatar = ((ImageDetail) detail).getBytes();
if (avatar == null) vCard.setAvatar(new byte[0]);
else vCard.setAvatar(avatar);
fireServerStoredDetailsChangeEvent(
jabberProvider,
ServerStoredDetailsChangeEvent.DETAIL_ADDED,
null,
detail);
}
else if (detail.getClass().equals(FirstNameDetail.class))
vCard.setFirstName((String)detail.getDetailValue());
else if (detail.getClass().equals(MiddleNameDetail.class))
vCard.setMiddleName((String)detail.getDetailValue());
else if (detail.getClass().equals(LastNameDetail.class))
vCard.setLastName((String)detail.getDetailValue());
else if (detail.getClass().equals(NicknameDetail.class))
vCard.setNickName((String)detail.getDetailValue());
else if (detail.getClass().equals(URLDetail.class))
{
if (detail.getDetailValue() != null)
vCard.setField(
"URL", ((URL)detail.getDetailValue()).toString());
}
else if (detail.getClass().equals(BirthDateDetail.class))
{
if (detail.getDetailValue() != null)
{
Calendar c = ((BirthDateDetail)detail).getCalendar();
DateFormat dateFormat =
new SimpleDateFormat(
JabberActivator.getResources().getI18NString(
"plugin.accountinfo.BDAY_FORMAT"));
String strdate = dateFormat.format(c.getTime());
vCard.setField("BDAY", strdate);
}
}
else if (detail.getClass().equals(AddressDetail.class))
vCard.setAddressFieldHome(
"STREET", (String)detail.getDetailValue());
else if (detail.getClass().equals(CityDetail.class))
vCard.setAddressFieldHome(
"LOCALITY", (String)detail.getDetailValue());
else if (detail.getClass().equals(ProvinceDetail.class))
vCard.setAddressFieldHome(
"REGION", (String)detail.getDetailValue());
else if (detail.getClass().equals(PostalCodeDetail.class))
vCard.setAddressFieldHome(
"PCODE", (String)detail.getDetailValue());
else if (detail.getClass().equals(CountryDetail.class))
vCard.setAddressFieldHome(
"CTRY", (String)detail.getDetailValue());
else if (detail.getClass().equals(PhoneNumberDetail.class))
vCard.setPhoneHome("VOICE", (String)detail.getDetailValue());
else if (detail.getClass().equals(WorkPhoneDetail.class))
vCard.setPhoneWork("VOICE", (String)detail.getDetailValue());
else if (detail.getClass().equals(EmailAddressDetail.class))
vCard.setEmailHome((String)detail.getDetailValue());
else if (detail.getClass().equals(WorkEmailAddressDetail.class))
vCard.setEmailWork((String)detail.getDetailValue());
else if (detail.getClass().equals(WorkOrganizationNameDetail.class))
vCard.setOrganization((String)detail.getDetailValue());
else if (detail.getClass().equals(JobTitleDetail.class))
vCard.setField("TITLE", (String)detail.getDetailValue());
else if (detail.getClass().equals(AboutMeDetail.class))
vCard.setField("ABOUTME", (String)detail.getDetailValue());
}
try
{
// Push the new avatar photo to the server.
return this.uploadImageDetail(
ServerStoredDetailsChangeEvent.DETAIL_REPLACED,
currentDetailValue,
newDetailValue);
vCard.save(jabberProvider.getConnection());
}
catch (XMPPException xmppe)
{
logger.error("Error loading/saving vcard: ", xmppe);
throw new OperationFailedException(
"Error loading/saving vcard: ", 1, xmppe);
}
}
/**
* Determines whether the underlying implementation supports edition
* of this detail class.
* <p>
* @param detailClass the class whose edition we'd like to determine if it's
* possible
* @return true if the underlying implementation supports edition of this
* type of detail and false otherwise.
*/
public boolean isDetailClassEditable(
Class<? extends GenericDetail> detailClass)
{
if (isDetailClassSupported(detailClass)) {
return true;
}
return false;
}
/**
* Utility method throwing an exception if the icq stack is not properly
* Utility method throwing an exception if the jabber stack is not properly
* initialized.
* @throws java.lang.IllegalStateException if the underlying ICQ stack is
* @throws java.lang.IllegalStateException if the underlying jabber stack is
* not registered and initialized.
*/
private void assertConnected() throws IllegalStateException
@ -333,73 +439,4 @@ private void assertConnected() throws IllegalStateException
"The jabber provider must be signed on before "
+"being able to communicate.");
}
/**
* Uploads the new avatar image to the server via the vCard mechanism
* (XEP-0153).
*
* @param changeEventID the int ID of the event to dispatch
* @param currentDetailValue the detail value we'd like to replace.
* @param newDetailValue the value of the detail that we'd like to replace
* currentDetailValue with. If ((ImageDetail) newDetailValue).getBytes() is
* null, then this function removes the current avatar from the server by
* sending a vCard with a "photo" tag without any content.
*
* @return "true" if the new avatar image has been uploaded (even if the
* current avatar image is removed). "false" if an XMPPException occurs.
*/
private boolean uploadImageDetail(
int changeEventID,
ServerStoredDetails.GenericDetail currentDetailValue,
ServerStoredDetails.GenericDetail newDetailValue)
{
boolean isPhotoChanged = false;
try
{
byte[] newAvatar = ((ImageDetail) newDetailValue).getBytes();
VCardXEP0153 v1 = new VCardXEP0153();
// Retrieve the old vCard.
v1.load(jabberProvider.getConnection());
// Checks if the new avatar photo is diferent form the server one.
// If yes, then upload the new avatar photo.
if(!Arrays.equals(v1.getAvatar(), newAvatar))
{
if(newAvatar == null)
{
v1.setAvatar(new byte[0]);
}
else
{
v1.setAvatar(newAvatar);
}
// Saves the new vCard.
v1.save(jabberProvider.getConnection());
}
// Sets the new avatar photo advertised in all presence messages,
// and send one presence messge immediately.
((OperationSetPersistentPresenceJabberImpl)
this.jabberProvider.getOperationSet(
OperationSetPersistentPresence.class))
.updateAccountPhotoPresenceExtension(newAvatar);
// Advertises all detail change listeners, that the server stored
// details have changed.
fireServerStoredDetailsChangeEvent(
jabberProvider,
changeEventID,
currentDetailValue,
newDetailValue);
isPhotoChanged = true;
}
catch (XMPPException xmppe)
{
logger.error("Error loading/saving vcard: ", xmppe);
}
return isPhotoChanged;
}
}
}

@ -267,6 +267,23 @@ public boolean isDetailClassSupported(
return false;
}
/**
* Determines whether the underlying implementation supports edition
* of this detail class.
* <p>
* @param detailClass the class whose edition we'd like to determine if it's
* possible
* @return true if the underlying implementation supports edition of this
* type of detail and false otherwise.
*/
public boolean isDetailClassEditable(
Class<? extends GenericDetail> detailClass)
{
return
isDetailClassSupported(detailClass)
&& ImageDetail.class.isAssignableFrom(detailClass);
}
/**
* The method returns the number of instances supported for a particular
* detail type. Some protocols offer storing multiple values for a
@ -518,6 +535,18 @@ public boolean replaceDetail(
return false;
}
/*
* (non-Javadoc)
* @see net.java.sip.communicator.service.protocol.OperationSetServerStoredAccountInfo#save()
* This method is currently unimplemented.
* The idea behind this method is for users to call it only once, meaning
* that all ServerStoredDetails previously modified by addDetail/removeDetail
* and/or replaceDetail will be saved online on the server in one step.
* Currently, addDetail/removeDetail/replaceDetail methods are doing the
* actual saving but in the future the saving part must be carried here.
*/
public void save() throws OperationFailedException {}
/**
* Utility method throwing an exception if the icq stack is not properly
* initialized.

@ -194,6 +194,23 @@ && isImageDetailSupported())
|| DisplayNameDetail.class.isAssignableFrom(detailClass);
}
/**
* Determines whether the underlying implementation supports the edition
* of this detail class.
* <p>
* @param detailClass the class whose edition we'd like to determine if it's
* possible
* @return true if the underlying implementation supports edition of this
* type of detail and false otherwise.
*/
public boolean isDetailClassEditable(
Class<? extends GenericDetail> detailClass)
{
return
isDetailClassSupported(detailClass)
&& ImageDetail.class.isAssignableFrom(detailClass);
}
/**
* The method returns the number of instances supported for a particular
* detail type.
@ -411,6 +428,18 @@ public boolean replaceDetail(
return true;
}
/*
* (non-Javadoc)
* @see net.java.sip.communicator.service.protocol.OperationSetServerStoredAccountInfo#save()
* This method is currently unimplemented.
* The idea behind this method is for users to call it only once, meaning
* that all ServerStoredDetails previously modified by addDetail/removeDetail
* and/or replaceDetail will be saved online on the server in one step.
* Currently, addDetail/removeDetail/replaceDetail methods are doing the
* actual saving but in the future the saving part must be carried here.
*/
public void save() throws OperationFailedException {}
/**
* Determines if image details is supported.
*

@ -8,7 +8,8 @@
import java.util.*;
import net.java.sip.communicator.service.browserlauncher.*;
import net.java.sip.communicator.service.globaldisplaydetails.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
@ -18,6 +19,7 @@
* Starts the account info bundle.
*
* @author Adam Glodstein
* @author Marin Dzhigarov
*/
public class AccountInfoActivator
implements BundleActivator
@ -30,21 +32,52 @@ public class AccountInfoActivator
*/
public static BundleContext bundleContext;
private static BrowserLauncherService browserLauncherService;
private static GlobalDisplayDetailsService globalDisplayDetailsService;
public void start(BundleContext bc) throws Exception
{
AccountInfoActivator.bundleContext = bc;
// new LazyConfigurationForm(
// "net.java.sip.communicator.plugin.accountinfo.AccountInfoPanel",
// getClass().getClassLoader(), "plugin.accountinfo.PLUGIN_ICON",
// "plugin.accountinfo.TITLE");
Hashtable<String, String> containerFilter
= new Hashtable<String, String>();
containerFilter.put(
Container.CONTAINER_ID,
Container.CONTAINER_TOOLS_MENU.getID());
bundleContext.registerService(
PluginComponentFactory.class.getName(),
new PluginComponentFactory(Container.CONTAINER_TOOLS_MENU)
{
@Override
protected PluginComponent getPluginInstance()
{
return new AccountInfoMenuItemComponent(
getContainer(), this);
}
},
containerFilter);
containerFilter = new Hashtable<String, String>();
containerFilter.put(
Container.CONTAINER_ID,
Container.CONTAINER_ACCOUNT_RIGHT_BUTTON_MENU.getID());
bundleContext.registerService(
PluginComponentFactory.class.getName(),
new PluginComponentFactory(
Container.CONTAINER_ACCOUNT_RIGHT_BUTTON_MENU)
{
@Override
protected PluginComponent getPluginInstance()
{
return new AccountInfoMenuItemComponent(
getContainer(), this);
}
},
containerFilter);
}
public void stop(BundleContext bc) throws Exception
{
}
public void stop(BundleContext bc) throws Exception {}
/**
* Returns all <tt>ProtocolProviderFactory</tt>s obtained from the bundle
@ -53,7 +86,8 @@ public void stop(BundleContext bc) throws Exception
* @return all <tt>ProtocolProviderFactory</tt>s obtained from the bundle
* context
*/
public static Map<Object, ProtocolProviderFactory> getProtocolProviderFactories()
public static Map<Object, ProtocolProviderFactory>
getProtocolProviderFactories()
{
Map<Object, ProtocolProviderFactory> providerFactoriesMap =
new Hashtable<Object, ProtocolProviderFactory>();
@ -87,23 +121,21 @@ public static Map<Object, ProtocolProviderFactory> getProtocolProviderFactories(
}
/**
* Returns the <tt>BrowserLauncherService</tt> currently registered.
* Returns the <tt>GlobalDisplayDetailsService</tt> obtained from the bundle
* context.
*
* @return the <tt>BrowserLauncherService</tt>
* @return the <tt>GlobalDisplayDetailsService</tt> obtained from the bundle
* context
*/
public static BrowserLauncherService getBrowserLauncher()
public static GlobalDisplayDetailsService getGlobalDisplayDetailsService()
{
if (browserLauncherService == null)
if (globalDisplayDetailsService == null)
{
ServiceReference serviceReference =
bundleContext.getServiceReference(BrowserLauncherService.class
.getName());
browserLauncherService =
(BrowserLauncherService) bundleContext
.getService(serviceReference);
globalDisplayDetailsService
= ServiceUtils.getService(
bundleContext,
GlobalDisplayDetailsService.class);
}
return browserLauncherService;
return globalDisplayDetailsService;
}
}

@ -0,0 +1,119 @@
/*
* 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.plugin.accountinfo;
import java.awt.event.*;
import javax.swing.*;
import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.protocol.*;
/**
* Implements <tt>PluginComponent</tt> for the "Account Info" menu item.
*
* @author Marin Dzhigarov
*/
public class AccountInfoMenuItemComponent
extends AbstractPluginComponent
{
/**
* The "Account Info" menu item.
*/
JMenuItem accountInfoMenuItem;
/**
* The dialog that appears when "Account Info" menu item is clicked.
*/
static final SIPCommDialog dialog = new SIPCommDialog() {
/**
* Serial version UID.
*/
private static final long serialVersionUID = 1L;
/**
* Presses programmatically the cancel button, when Esc key is pressed.
*
* @param isEscaped indicates if the Esc button was pressed on close
*/
protected void close(boolean isEscaped)
{
this.setVisible(false);
}
};
/**
* The main panel containing account information.
*/
static final AccountInfoPanel accountInfoPanel = new AccountInfoPanel();
/**
* Initializes a new "Account Info" menu item.
*
* @param container the container of the update menu component
*/
public AccountInfoMenuItemComponent(Container container,
PluginComponentFactory parentFactory)
{
super(container, parentFactory);
AccountInfoActivator.bundleContext.addServiceListener(accountInfoPanel);
dialog.setPreferredSize(new java.awt.Dimension(600, 400));
dialog.setTitle(Resources.getString("plugin.accountinfo.TITLE"));
dialog.add(accountInfoPanel);
}
public void setCurrentAccountID(AccountID accountID)
{
accountInfoMenuItem.setEnabled(
accountID != null && accountID.isEnabled());
accountInfoPanel.getAccountsComboBox().setSelectedItem(
accountInfoPanel.getAccountsTable().get(accountID));
}
/**
* Gets the UI <tt>Component</tt> of this <tt>PluginComponent</tt>.
*
* @return the UI <tt>Component</tt> of this <tt>PluginComponent</tt>
* @see PluginComponent#getComponent()
*/
public Object getComponent()
{
if(accountInfoMenuItem == null)
{
accountInfoMenuItem
= new JMenuItem(
Resources.getString("plugin.accountinfo.TITLE"));
accountInfoMenuItem.setIcon(
Resources.getImage(
"plugin.contactinfo.CONTACT_INFO_ICON"));
accountInfoMenuItem.addActionListener(
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
dialog.setVisible(true);
accountInfoPanel.setVisible(true);
}
});
}
return accountInfoMenuItem;
}
/**
* Gets the name of this <tt>PluginComponent</tt>.
*
* @return the name of this <tt>PluginComponent</tt>
* @see PluginComponent#getName()
*/
public String getName()
{
return
Resources.getString("plugin.accountinfo.TITLE");
}
}

@ -6,6 +6,7 @@
package net.java.sip.communicator.plugin.accountinfo;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
@ -17,13 +18,15 @@
import org.osgi.framework.*;
/**
* A GUI plug-in for SIP Communicator that will allow users to set cross
* A GUI plug-in for Jitsi that will allow users to set cross
* protocol account information.
*
* @author Adam Goldstein
* @author Marin Dzhigarov
*/
public class AccountInfoPanel
extends TransparentPanel
implements ServiceListener
{
/**
* Serial version UID.
@ -31,25 +34,86 @@ public class AccountInfoPanel
private static final long serialVersionUID = 0L;
/**
* The right side of the AccountInfo frame that contains protocol specific
* account details.
* The panel that contains the currently active <tt>AccountDetailsPanel</tt>
*/
private AccountDetailsPanel detailsPanel;
private final JPanel centerPanel =
new TransparentPanel(new BorderLayout(10, 10));
private final Map<ProtocolProviderService, AccountDetailsPanel> accountsTable =
new Hashtable<ProtocolProviderService, AccountDetailsPanel>();
/**
* The currently active <tt>AccountDetailsPanel</tt>
*/
private AccountDetailsPanel currentDetailsPanel;
/**
* Combo box that is used for switching between accounts.
*/
private final JComboBox accountsComboBox;
/**
* Constructs a frame with an AccuontInfoAccountPanel to display all
* registered accounts on the left, and an information interface,
* AccountDetailsPanel, on the right.
* Instances of the <tt>AccountDetailsPanel</tt> are created for every
* registered <tt>AccountID</tt>. All such pairs are stored in
* this map.
*/
private final Map<AccountID, AccountDetailsPanel>
accountsTable =
new HashMap<AccountID, AccountDetailsPanel>();
/**
* Creates an instance of <tt>AccountInfoPanel</tt> that contains combo box
* component with active user accounts and <tt>AccountDetailsPanel</tt> to
* display and edit account information.
*/
public AccountInfoPanel()
{
super(new BorderLayout());
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
accountsComboBox = new JComboBox();
accountsComboBox.addItemListener(new ItemListener()
{
@Override
public void itemStateChanged(ItemEvent e)
{
if (e.getStateChange() == ItemEvent.SELECTED)
{
AccountDetailsPanel panel =
(AccountDetailsPanel) e.getItem();
panel.setOpaque(false);
centerPanel.removeAll();
centerPanel.add(panel, BorderLayout.CENTER);
centerPanel.revalidate();
centerPanel.repaint();
currentDetailsPanel = panel;
}
}
});
init();
centerPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
ComboBoxRenderer renderer = new ComboBoxRenderer();
accountsComboBox.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
accountsComboBox.setRenderer(renderer);
JLabel comboLabel = new JLabel(
Resources.getString(
"plugin.accountinfo.SELECT_ACCOUNT"));
comboLabel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
JTabbedPane accountsTabbedPane = new SIPCommTabbedPane();
JPanel comboBoxPanel = new TransparentPanel();
comboBoxPanel.setLayout(new BoxLayout(comboBoxPanel, BoxLayout.X_AXIS));
comboBoxPanel.setBorder(
BorderFactory.createEmptyBorder(10, 10, 10, 10));
comboBoxPanel.add(comboLabel);
comboBoxPanel.add(accountsComboBox);
add(comboBoxPanel);
add(centerPanel);
}
private void init()
{
for (ProtocolProviderFactory providerFactory : AccountInfoActivator
.getProtocolProviderFactories().values())
{
@ -63,22 +127,56 @@ public AccountInfoPanel()
{
serRef = providerFactory.getProviderForAccount(accountID);
protocolProvider = (ProtocolProviderService) AccountInfoActivator
protocolProvider = (ProtocolProviderService)AccountInfoActivator
.bundleContext.getService(serRef);
detailsPanel = new AccountDetailsPanel(protocolProvider);
currentDetailsPanel = new AccountDetailsPanel(protocolProvider);
accountsTable.put(
protocolProvider.getAccountID(), currentDetailsPanel);
accountsTable.put(protocolProvider, detailsPanel);
accountsComboBox.addItem(currentDetailsPanel);
protocolProvider.addRegistrationStateChangeListener(
new RegistrationStateChangeListenerImpl());
accountsTabbedPane.addTab(
accountID.getUserID(), detailsPanel);
}
}
}
/**
* A custom renderer to display properly <tt>AccountDetailsPanel</tt>
* in a combo box.
*/
private class ComboBoxRenderer extends DefaultListCellRenderer
{
/**
* Serial version UID.
*/
private static final long serialVersionUID = 0L;
this.add(accountsTabbedPane, BorderLayout.CENTER);
@Override
public Component getListCellRendererComponent(
JList list, Object value, int index,
boolean isSelected, boolean hasFocus)
{
JLabel renderer
= (JLabel) super.getListCellRendererComponent(
list, value, index, isSelected, hasFocus);
if (value != null)
{
AccountDetailsPanel panel = (AccountDetailsPanel) value;
renderer.setText(
panel.protocolProvider.getAccountID().getUserID());
ImageIcon protocolIcon =
new ImageIcon(panel.protocolProvider.getProtocolIcon().
getIcon((ProtocolIcon.ICON_SIZE_16x16)));
renderer.setIcon(protocolIcon);
}
return renderer;
}
}
private class RegistrationStateChangeListenerImpl
@ -88,19 +186,117 @@ public void registrationStateChanged(RegistrationStateChangeEvent evt)
{
ProtocolProviderService protocolProvider = evt.getProvider();
if (protocolProvider.getOperationSet(
OperationSetServerStoredAccountInfo.class) != null
&& evt.getNewState() == RegistrationState.REGISTERED)
if (evt.getNewState() == RegistrationState.REGISTERED)
{
if (accountsTable.containsKey(protocolProvider))
if (accountsTable.containsKey(protocolProvider.getAccountID()))
{
AccountDetailsPanel detailsPanel
= accountsTable.get(protocolProvider);
= accountsTable.get(protocolProvider.getAccountID());
detailsPanel.loadDetails();
}
else
{
AccountDetailsPanel panel =
new AccountDetailsPanel(protocolProvider);
accountsTable.put(protocolProvider.getAccountID(), panel);
accountsComboBox.addItem(panel);
}
}
else if (evt.getNewState() == RegistrationState.UNREGISTERING)
{
AccountDetailsPanel panel
= accountsTable.get(protocolProvider.getAccountID());
if (panel != null)
{
accountsTable.remove(protocolProvider.getAccountID());
accountsComboBox.removeItem(panel);
if (currentDetailsPanel == panel)
{
currentDetailsPanel = null;
centerPanel.removeAll();
centerPanel.revalidate();
centerPanel.repaint();
}
}
}
}
}
/**
* Handles registration and unregistration of
* <tt>ProtocolProviderService</tt>
*
* @param event
*/
@Override
public void serviceChanged(ServiceEvent event)
{
// Get the service from the event.
Object service
= AccountInfoActivator.bundleContext.getService(
event.getServiceReference());
if(!detailsPanel.isDataLoaded())
detailsPanel.loadDetails();
// We are not interested in any services
// other than ProtocolProviderService
if (!(service instanceof ProtocolProviderService))
return;
ProtocolProviderService protocolProvider =
(ProtocolProviderService) service;
// If a new protocol provider is registered we to add new
// AccountDetailsPanel to the combo box containing active accounts.
if (event.getType() == ServiceEvent.REGISTERED)
{
if (accountsTable.get(protocolProvider.getAccountID()) == null)
{
AccountDetailsPanel panel =
new AccountDetailsPanel(protocolProvider);
accountsTable.put(protocolProvider.getAccountID(), panel);
accountsComboBox.addItem(panel);
protocolProvider.addRegistrationStateChangeListener(
new RegistrationStateChangeListenerImpl());
}
}
// If the protocol provider is being unregistered we have to remove
// a AccountDetailsPanel from the combo box containing active accounts.
else if (event.getType() == ServiceEvent.UNREGISTERING)
{
AccountDetailsPanel panel
= accountsTable.get(protocolProvider.getAccountID());
if (panel != null)
{
accountsTable.remove(protocolProvider.getAccountID());
accountsComboBox.removeItem(panel);
if (currentDetailsPanel == panel)
{
currentDetailsPanel = null;
centerPanel.removeAll();
centerPanel.revalidate();
centerPanel.repaint();
}
}
}
}
/**
* Returns the combo box that switches between account detail panels.
*
* @return The combo box that switches between account detail panels.
*/
public JComboBox getAccountsComboBox()
{
return accountsComboBox;
}
/**
* Returns mapping between registered AccountIDs and their respective
* AccountDetailsPanel that contains all the details for the account.
*
* @return mapping between registered AccountIDs and AccountDetailsPanel.
*/
public Map<AccountID, AccountDetailsPanel> getAccountsTable()
{
return accountsTable;
}
}

@ -5,27 +5,19 @@ Bundle-Vendor: jitsi.org
Bundle-Version: 0.0.1
System-Bundle: yes
Import-Package: org.osgi.framework,
net.java.sip.communicator.service.browserlauncher,
net.java.sip.communicator.service.contactlist,
net.java.sip.communicator.service.contactlist.event,
net.java.sip.communicator.service.gui,
net.java.sip.communicator.service.gui.event,
net.java.sip.communicator.service.globaldisplaydetails,
net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.event,
org.jitsi.service.resources, net.java.sip.communicator.service.resources,
org.jitsi.service.resources,
org.jitsi.util,
net.java.sip.communicator.service.resources,
net.java.sip.communicator.util,
net.java.sip.communicator.util.skin,
net.java.sip.communicator.plugin.desktoputil,
net.java.sip.communicator.plugin.desktoputil.presence.avatar,
javax.swing,
javax.swing.event,
javax.swing.table,
javax.swing.border,
javax.swing.text,
javax.swing.text.html,
javax.accessibility,
javax.swing.plaf,
javax.swing.plaf.metal,
javax.swing.plaf.basic,
javax.imageio,
javax.swing.filechooser,
javax.swing.tree,
javax.swing.undo,
javax.swing.border
javax.imageio

@ -12,10 +12,14 @@
import net.java.sip.communicator.service.credentialsstorage.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.keybindings.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.resources.*;
import net.java.sip.communicator.util.*;
import org.jitsi.service.audionotifier.*;
import org.jitsi.service.configuration.*;
import org.jitsi.service.fileaccess.*;
import org.jitsi.service.neomedia.*;
import org.jitsi.service.resources.*;
import org.osgi.framework.*;
@ -40,6 +44,14 @@ public class DesktopUtilActivator
private static UIService uiService;
private static AccountManager accountManager;
private static FileAccessService fileAccessService;
private static MediaService mediaService;
private static AudioNotifierService audioNotifierService;
static BundleContext bundleContext;
/**
@ -247,4 +259,69 @@ public VerifyCertificateDialog createDialog(
{
return new VerifyCertificateDialogImpl(certs, title, message);
}
/**
* Returns the <tt>AccountManager</tt> obtained from the bundle context.
* @return the <tt>AccountManager</tt> obtained from the bundle context
*/
public static AccountManager getAccountManager()
{
if(accountManager == null)
{
accountManager
= ServiceUtils.getService(bundleContext, AccountManager.class);
}
return accountManager;
}
/**
* Returns the <tt>FileAccessService</tt> obtained from the bundle context.
*
* @return the <tt>FileAccessService</tt> obtained from the bundle context
*/
public static FileAccessService getFileAccessService()
{
if (fileAccessService == null)
{
fileAccessService
= ServiceUtils.getService(
bundleContext,
FileAccessService.class);
}
return fileAccessService;
}
/**
* Returns an instance of the <tt>MediaService</tt> obtained from the
* bundle context.
* @return an instance of the <tt>MediaService</tt> obtained from the
* bundle context
*/
public static MediaService getMediaService()
{
if (mediaService == null)
{
mediaService
= ServiceUtils.getService(bundleContext, MediaService.class);
}
return mediaService;
}
/**
* Returns the <tt>AudioNotifierService</tt> obtained from the bundle
* context.
* @return the <tt>AudioNotifierService</tt> obtained from the bundle
* context
*/
public static AudioNotifierService getAudioNotifier()
{
if (audioNotifierService == null)
{
audioNotifierService
= ServiceUtils.getService(
bundleContext,
AudioNotifierService.class);
}
return audioNotifierService;
}
}

@ -142,14 +142,17 @@ public void paintComponent(Graphics g)
null);
}
int frameWidth = frameImage.getWidth(this);
int frameHeight = frameImage.getHeight(this);
if ((frameWidth != -1) && (frameHeight != -1))
g.drawImage(
frameImage,
width / 2 - frameWidth / 2,
height / 2 - frameHeight / 2,
null);
if (frameImage != null)
{
int frameWidth = frameImage.getWidth(this);
int frameHeight = frameImage.getHeight(this);
if ((frameWidth != -1) && (frameHeight != -1))
g.drawImage(
frameImage,
width / 2 - frameWidth / 2,
height / 2 - frameHeight / 2,
null);
}
}
/**
@ -157,14 +160,21 @@ public void paintComponent(Graphics g)
*/
public void loadSkin()
{
this.frameImage
= ImageUtils
.scaleImageWithinBounds(
DesktopUtilActivator
.getResources()
.getImage("service.gui.USER_PHOTO_FRAME").getImage(),
width,
height);
ImageIcon frameIcon = DesktopUtilActivator
.getResources()
.getImage("service.gui.USER_PHOTO_FRAME");
// Frame image will be drawn only if's bigger or equal to the underlying
// image. We would like to avoid pixelated results!
if (frameIcon.getIconWidth() >= width
&& frameIcon.getIconHeight() >= frameIcon.getIconHeight())
{
this.frameImage
= ImageUtils
.scaleImageWithinBounds(frameIcon.getImage(),
width,
height);
}
}
/**

@ -34,11 +34,6 @@ public class FramedImageWithMenu
*/
private JPopupMenu popupMenu;
/**
* The parent frame.
*/
private JFrame mainFrame;
/**
* Should we currently draw overlay.
*/
@ -62,14 +57,12 @@ public class FramedImageWithMenu
* @param height height of component.
*/
public FramedImageWithMenu(
JFrame mainFrame,
ImageIcon imageIcon,
int width,
int height)
{
super(imageIcon, width, height);
this.mainFrame = mainFrame;
this.addMouseListener(this);
}
@ -189,9 +182,9 @@ private void showDialog(MouseEvent e, boolean show)
if (show)
{
Point imageLoc = this.getLocationOnScreen();
Point rootPaneLoc = mainFrame.getRootPane().getLocationOnScreen();
Point rootPaneLoc = this.getRootPane().getLocationOnScreen();
this.popupMenu.setSize(mainFrame.getRootPane().getWidth(),
this.popupMenu.setSize(this.getRootPane().getWidth(),
this.popupMenu.getHeight());
this.popupMenu.show(this, (rootPaneLoc.x - imageLoc.x),
@ -206,7 +199,7 @@ private void showDialog(MouseEvent e, boolean show)
public void mouseEntered(MouseEvent e)
{
if (this.drawOverlay)
if (this.drawOverlay || !this.isEnabled())
return;
this.drawOverlay = true;
@ -222,7 +215,7 @@ public void mouseEntered(MouseEvent e)
public void mouseExited(MouseEvent e)
{
// Remove overlay only if the dialog isn't visible
if (!popupMenu.isVisible())
if (!popupMenu.isVisible() && this.isEnabled())
{
this.drawOverlay = false;
this.repaint();
@ -231,7 +224,8 @@ public void mouseExited(MouseEvent e)
public void mouseReleased(MouseEvent e)
{
showDialog(e, !popupMenu.isVisible());
if (this.isEnabled())
showDialog(e, !popupMenu.isVisible());
}
/**

@ -30,6 +30,7 @@ Import-Package: com.sun.awt,
javax.xml.transform.dom,
javax.xml.transform.stream,
net.java.sip.communicator.util,
net.java.sip.communicator.util.account,
net.java.sip.communicator.util.skin,
net.java.sip.communicator.util.wizard,
net.java.sip.communicator.service.certificate,
@ -64,5 +65,6 @@ Export-Package: net.java.sip.communicator.plugin.desktoputil,
net.java.sip.communicator.plugin.desktoputil.event,
net.java.sip.communicator.plugin.desktoputil.plaf,
net.java.sip.communicator.plugin.desktoputil.presence,
net.java.sip.communicator.plugin.desktoputil.presence.avatar,
net.java.sip.communicator.plugin.desktoputil.transparent,
net.java.sip.communicator.plugin.desktoputil.wizard

@ -4,18 +4,18 @@
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.presence.avatar;
package net.java.sip.communicator.plugin.desktoputil.presence.avatar;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import org.jitsi.service.fileaccess.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.util.*;
import org.jitsi.service.fileaccess.*;
/**
* Take cares of storing(deleting, moving) images with the given indexes.
*/
@ -44,8 +44,9 @@ public static void deleteImage(int index)
try
{
File imageFile
= GuiActivator.getFileAccessService().getPrivatePersistentFile(
fileName, FileCategory.CACHE);
= DesktopUtilActivator.getFileAccessService()
.getPrivatePersistentFile(
fileName);
if (imageFile.exists() && !imageFile.delete())
logger.error("Failed to delete stored image at index " + index);
@ -71,8 +72,9 @@ public static BufferedImage loadImage(int index)
String imagePath = STORE_DIR + index + ".png";
imageFile
= GuiActivator.getFileAccessService().getPrivatePersistentFile(
imagePath, FileCategory.CACHE);
= DesktopUtilActivator.getFileAccessService().
getPrivatePersistentFile(
imagePath);
}
catch (Exception e)
{
@ -107,14 +109,12 @@ private static void moveImage(int oldIndex, int newIndex)
try
{
FileAccessService fas = GuiActivator.getFileAccessService();
File oldFile = fas.getPrivatePersistentFile(oldImagePath,
FileCategory.CACHE);
FileAccessService fas = DesktopUtilActivator.getFileAccessService();
File oldFile = fas.getPrivatePersistentFile(oldImagePath);
if (oldFile.exists())
{
File newFile = fas.getPrivatePersistentFile(newImagePath,
FileCategory.CACHE);
File newFile = fas.getPrivatePersistentFile(newImagePath);
oldFile.renameTo(newFile);
}
@ -148,15 +148,13 @@ public static void storeImage(BufferedImage image, int index)
try
{
FileAccessService fas = GuiActivator.getFileAccessService();
File storeDir = fas.getPrivatePersistentDirectory(STORE_DIR,
FileCategory.CACHE);
FileAccessService fas = DesktopUtilActivator.getFileAccessService();
File storeDir = fas.getPrivatePersistentDirectory(STORE_DIR);
// if dir doesn't exist create it
storeDir.mkdirs();
File file = fas.getPrivatePersistentFile(imagePath,
FileCategory.CACHE);
File file = fas.getPrivatePersistentFile(imagePath);
ImageIO.write(image, "png", file);
}

@ -4,18 +4,20 @@
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.presence.avatar;
package net.java.sip.communicator.plugin.desktoputil.presence.avatar;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import javax.swing.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.presence.avatar.imagepicker.*;
import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.plugin.desktoputil.presence.avatar.imagepicker.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.ServerStoredDetails.GenericDetail;
import net.java.sip.communicator.service.protocol.ServerStoredDetails.ImageDetail;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.account.*;
@ -85,6 +87,12 @@ public class SelectAvatarMenu
*/
private FramedImageWithMenu avatarImage;
/**
* The AccountID that we want to select avatar for. Could be null if
* we want to select a global avatar.
*/
private AccountID accountID;
/**
* Creates the dialog.
* @param avatarImage the button that will trigger this menu.
@ -98,6 +106,11 @@ public SelectAvatarMenu(FramedImageWithMenu avatarImage)
this.pack();
}
public void setAccountID(AccountID accountID)
{
this.accountID = accountID;
}
/**
* Init visible components.
*/
@ -108,7 +121,7 @@ private void init()
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
// Title label
JLabel titleLabel = new JLabel(GuiActivator.getResources()
JLabel titleLabel = new JLabel(DesktopUtilActivator.getResources()
.getI18NString("service.gui.avatar.RECENT_ICONS"));
titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD));
@ -146,17 +159,17 @@ private void init()
Color linkColor = new JMenuItem().getForeground();
addActionButton(buttonsPanel, this,
GuiActivator.getResources().getI18NString(
DesktopUtilActivator.getResources().getI18NString(
"service.gui.avatar.CHOOSE_ICON"),
CHOSE_BUTTON_NAME,
linkColor);
addActionButton(buttonsPanel, this,
GuiActivator.getResources().getI18NString(
DesktopUtilActivator.getResources().getI18NString(
"service.gui.avatar.REMOVE_ICON"),
REMOVE_BUTTON_NAME,
linkColor);
addActionButton(buttonsPanel, this,
GuiActivator.getResources().getI18NString(
DesktopUtilActivator.getResources().getI18NString(
"service.gui.avatar.CLEAR_RECENT"),
CLEAR_BUTTON_NAME,
linkColor);
@ -267,11 +280,11 @@ private void setNewImage(final BufferedImage image)
public void run()
{
AccountManager accountManager
= GuiActivator.getAccountManager();
= DesktopUtilActivator.getAccountManager();
for(AccountID accountID : accountManager.getStoredAccounts())
for (AccountID accountID : accountManager.getStoredAccounts())
{
if(accountManager.isAccountLoaded(accountID))
if (accountManager.isAccountLoaded(accountID))
{
ProtocolProviderService protocolProvider
= AccountUtils.getRegisteredProviderForAccount(
@ -280,27 +293,78 @@ public void run()
if(protocolProvider != null
&& protocolProvider.isRegistered())
{
OperationSetAvatar opSetAvatar
= protocolProvider
.getOperationSet(OperationSetAvatar.class);
if(opSetAvatar != null)
// If account id is set this means that we want to
// edit our current account image, not the global
// avatar. Hence, we might not want to save this
// account image on the server yet. For example: in
// the account info plugin the user might set a new
// avatar and then click the cancel button.
if (SelectAvatarMenu.this.accountID != null)
{
byte[] imageByte = null;
// Sets new avatar if not null. Otherwise, the
// opSetAvatar.setAvatar(null) will removes the
// current one.
if(image != null)
{
imageByte = ImageUtils.toByteArray(image);
}
try
if (accountID.equals(
SelectAvatarMenu.this.accountID))
{
opSetAvatar.setAvatar(imageByte);
OperationSetServerStoredAccountInfo opSet =
protocolProvider.getOperationSet(
OperationSetServerStoredAccountInfo.class);
if (opSet != null)
{
byte[] imageByte = null;
if (image != null)
{
imageByte =
ImageUtils.toByteArray(image);
}
avatarImage.setImageIcon(imageByte);
ImageDetail newDetail =
new ImageDetail(
"avatar", imageByte);
Iterator<GenericDetail> oldDetail =
opSet.getDetails(ImageDetail.class);
try
{
if (oldDetail.hasNext())
{
opSet.replaceDetail(
oldDetail.next(),
newDetail);
}
else
opSet.addDetail(newDetail);
}
catch (Throwable t)
{
logger.error(
"Error setting image", t);
}
}
}
catch(Throwable t)
}
else
{
OperationSetAvatar opSetAvatar
= protocolProvider
.getOperationSet(OperationSetAvatar.class);
if(opSetAvatar != null)
{
logger.error("Error setting image", t);
byte[] imageByte = null;
// Sets new avatar if not null. Otherwise, the
// opSetAvatar.setAvatar(null) will removes the
// current one.
if(image != null)
{
imageByte = ImageUtils.toByteArray(image);
}
try
{
opSetAvatar.setAvatar(imageByte);
}
catch(Throwable t)
{
logger.error("Error setting image", t);
}
}
}
}

@ -4,7 +4,7 @@
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.presence.avatar.imagepicker;
package net.java.sip.communicator.plugin.desktoputil.presence.avatar.imagepicker;
import java.awt.*;
import java.awt.event.*;
@ -13,7 +13,6 @@
import javax.swing.*;
import javax.swing.event.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.plugin.desktoputil.*;
/**
@ -53,15 +52,15 @@ public EditPanel(int clippingZoneWidth, int clippingZoneHeight)
this.clippingZoneWidth = clippingZoneWidth;
this.clippingZoneHeight = clippingZoneHeight;
this.zoomOut = new JButton(GuiActivator.getResources()
this.zoomOut = new JButton(DesktopUtilActivator.getResources()
.getImage("service.gui.buttons.ZOOM_OUT"));
this.zoomOut.addActionListener(this);
this.zoomIn = new JButton(GuiActivator.getResources()
this.zoomIn = new JButton(DesktopUtilActivator.getResources()
.getImage("service.gui.buttons.ZOOM_IN"));
this.zoomIn.addActionListener(this);
this.reset = new JButton(GuiActivator.getResources()
this.reset = new JButton(DesktopUtilActivator.getResources()
.getImage("service.gui.buttons.RESET"));
this.reset.setToolTipText(GuiActivator.getResources()
this.reset.setToolTipText(DesktopUtilActivator.getResources()
.getI18NString("service.gui.avatar.imagepicker.RESET"));
this.reset.addActionListener(this);
@ -69,7 +68,7 @@ public EditPanel(int clippingZoneWidth, int clippingZoneHeight)
clippingZoneWidth);
imageSizeSlider.addChangeListener(this);
imageSizeSlider.setOpaque(false);
imageSizeSlider.setToolTipText(GuiActivator.getResources()
imageSizeSlider.setToolTipText(DesktopUtilActivator.getResources()
.getI18NString("service.gui.avatar.imagepicker.IMAGE_SIZE"));
TransparentPanel sliderPanel = new TransparentPanel();

@ -4,7 +4,7 @@
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.presence.avatar.imagepicker;
package net.java.sip.communicator.plugin.desktoputil.presence.avatar.imagepicker;
import java.awt.*;
import java.awt.event.*;

@ -4,7 +4,7 @@
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.presence.avatar.imagepicker;
package net.java.sip.communicator.plugin.desktoputil.presence.avatar.imagepicker;
import java.awt.*;
import java.awt.event.*;
@ -14,7 +14,6 @@
import javax.imageio.*;
import javax.swing.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.plugin.desktoputil.*;
/**
@ -51,7 +50,7 @@ public ImagePickerDialog(int clipperZoneWidth, int clipperZoneHeight)
*/
private void initDialog()
{
this.setTitle(GuiActivator.getResources()
this.setTitle(DesktopUtilActivator.getResources()
.getI18NString("service.gui.avatar.imagepicker.IMAGE_PICKER"));
this.setModal(true);
this.setResizable(true);
@ -91,22 +90,22 @@ private void initComponents(int clipperZoneWidth, int clipperZoneHeight)
this.editPanel = new EditPanel(clipperZoneWidth, clipperZoneHeight);
// Buttons
this.okButton = new JButton(GuiActivator.getResources()
this.okButton = new JButton(DesktopUtilActivator.getResources()
.getI18NString("service.gui.avatar.imagepicker.SET"));
this.okButton.addActionListener(this);
this.okButton.setName("okButton");
this.cancelButton = new JButton(GuiActivator.getResources()
this.cancelButton = new JButton(DesktopUtilActivator.getResources()
.getI18NString("service.gui.avatar.imagepicker.CANCEL"));
this.cancelButton.addActionListener(this);
this.cancelButton.setName("cancelButton");
this.selectFileButton = new JButton(GuiActivator.getResources()
this.selectFileButton = new JButton(DesktopUtilActivator.getResources()
.getI18NString("service.gui.avatar.imagepicker.CHOOSE_FILE"));
this.selectFileButton.addActionListener(this);
this.selectFileButton.setName("selectFileButton");
this.webcamButton = new JButton(GuiActivator.getResources()
this.webcamButton = new JButton(DesktopUtilActivator.getResources()
.getI18NString("service.gui.avatar.imagepicker.TAKE_PHOTO"));
this.webcamButton.addActionListener(this);
@ -146,8 +145,9 @@ public void actionPerformed(ActionEvent e)
else if (name.equals("selectFileButton"))
{
SipCommFileChooser chooser = GenericFileDialog.create(
GuiActivator.getUIService().getMainFrame(),
GuiActivator.getResources().getI18NString(
//GuiActivator.getUIService().getMainFrame(),
null,
DesktopUtilActivator.getResources().getI18NString(
"service.gui.avatar.imagepicker.CHOOSE_FILE"),
SipCommFileChooser.LOAD_FILE_OPERATION);
@ -217,7 +217,7 @@ public boolean accept(File f)
@Override
public String getDescription()
{
return GuiActivator.getResources()
return DesktopUtilActivator.getResources()
.getI18NString("service.gui.avatar.imagepicker.IMAGE_FILES");
}
}

@ -4,7 +4,7 @@
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.presence.avatar.imagepicker;
package net.java.sip.communicator.plugin.desktoputil.presence.avatar.imagepicker;
import java.awt.*;
import java.awt.event.*;
@ -12,10 +12,8 @@
import javax.swing.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.plugin.desktoputil.TransparentPanel;
import net.java.sip.communicator.util.*;
import org.jitsi.service.audionotifier.*;
import org.jitsi.service.neomedia.*;
@ -51,7 +49,7 @@ public class WebcamDialog
public WebcamDialog(ImagePickerDialog parent)
{
super(false);
this.setTitle(GuiActivator.getResources()
this.setTitle(DesktopUtilActivator.getResources()
.getI18NString("service.gui.avatar.imagepicker.TAKE_PHOTO"));
this.setModal(true);
@ -66,13 +64,13 @@ public WebcamDialog(ImagePickerDialog parent)
private void init()
{
this.grabSnapshot = new JButton();
this.grabSnapshot.setText(GuiActivator.getResources()
this.grabSnapshot.setText(DesktopUtilActivator.getResources()
.getI18NString("service.gui.avatar.imagepicker.CLICK"));
this.grabSnapshot.setName("grab");
this.grabSnapshot.addActionListener(this);
this.grabSnapshot.setEnabled(false);
JButton cancelButton = new JButton(GuiActivator.getResources()
JButton cancelButton = new JButton(DesktopUtilActivator.getResources()
.getI18NString("service.gui.avatar.imagepicker.CANCEL"));
cancelButton.setName("cancel");
cancelButton.addActionListener(this);
@ -120,7 +118,7 @@ private void init()
private void initAccessWebcam()
{
//Call the method in the media service
MediaService mediaService = GuiActivator.getMediaService();
MediaService mediaService = DesktopUtilActivator.getMediaService();
this.videoContainer
= (Component)
@ -174,10 +172,10 @@ public byte[] getGrabbedImage()
*/
private void playSound()
{
String soundKey = GuiActivator.getResources()
String soundKey = DesktopUtilActivator.getResources()
.getSoundPath("WEBCAM_SNAPSHOT");
SCAudioClip audio = GuiActivator.getAudioNotifier()
SCAudioClip audio = DesktopUtilActivator.getAudioNotifier()
.createAudio(soundKey);
audio.play();

@ -261,6 +261,11 @@ public void setCurrentContactGroup(MetaContactGroup metaGroup)
}
public void setCurrentAccountID(AccountID account)
{
}
private static ImageIcon getLocaleIcon(Parameters.Locale locale,
boolean isAvailable)
{

@ -106,6 +106,13 @@ public void setCurrentContactGroup(MetaContactGroup metaGroup)
{
}
/*
* Implements PluginComponent#setCurrentAccountID(AccountID).
*/
public void setCurrentAccountID(AccountID accountID)
{
}
/**
* Returns the factory that has created the component.
* @return the parent factory.

@ -71,6 +71,12 @@ public class Container
public static final Container CONTAINER_CONTACT_RIGHT_BUTTON_MENU
= new Container("CONTAINER_CONTACT_RIGHT_BUTTON_MENU");
/**
* Accounts window "right button menu" over an account.
*/
public static final Container CONTAINER_ACCOUNT_RIGHT_BUTTON_MENU
= new Container("CONTAINER_ACCOUNT_RIGHT_BUTTON_MENU");
/**
* Main application window "right button menu" over a group container.
*/

@ -21,6 +21,11 @@
* implement the <tt>setCurrentContact</tt> and
* <tt>setCurrentContactGroup</tt> methods.
* <p>
* <p>
* All components interested in the current account that they're dealing
* with (i.g. the one selected in the account list for example), should
* implement the <tt>setCurrentAccountID</tt> method.
* <p>
* Note that <tt>getComponent</tt> should return a valid AWT, SWT or Swing
* control in order to appear properly in the GUI.
*
@ -76,6 +81,16 @@ public interface PluginComponent
*/
public void setCurrentContactGroup(MetaContactGroup metaGroup);
/**
* Sets the current AccountID. Meant to be used by plugin components that are
* interested in the current AccountID. The current AccountID could be that
* of a currently selected account in the account list. It depends on the
* container, where this component is meant to be added.
*
* @param account the current account.
*/
public void setCurrentAccountID(AccountID accountID);
/**
* Returns the factory that has created the component.
* @return the parent factory.

@ -107,6 +107,7 @@ public void setAvatar(byte[] avatar)
this.accountInfoOpSet.addDetail(newDetail);
else
this.accountInfoOpSet.replaceDetail(oldDetail, newDetail);
accountInfoOpSet.save();
} catch (OperationFailedException e)
{
logger.warn("Unable to set new avatar", e);

@ -115,6 +115,18 @@ public Iterator<GenericDetail> getDetails(
public boolean isDetailClassSupported(
Class<? extends GenericDetail> detailClass);
/**
* Determines whether the underlying implementation supports edition
* of this detail class.
* <p>
* @param detailClass the class whose edition we'd like to determine if it's
* possible
* @return true if the underlying implementation supports edition of this
* type of detail and false otherwise.
*/
public boolean isDetailClassEditable(
Class<? extends GenericDetail> detailClass);
/**
* The method returns the number of instances supported for a particular
* detail type. Some protocols offer storing mutliple values for a
@ -128,7 +140,7 @@ public int getMaxDetailInstances(
Class<? extends GenericDetail> detailClass);
/**
* Adds the specified detail to the list of details registered on-line
* Adds the specified detail to the list of details ready to be saved online
* for this account. If such a detail already exists its max instance number
* is consulted and if it allows it - a second instance is added or otherwise
* and illegal argument exception is thrown. An IllegalArgumentException is
@ -139,11 +151,9 @@ public int getMaxDetailInstances(
* @param detail the detail that we'd like registered on the server.
* <p>
* @throws IllegalArgumentException if such a detail already exists and its
* max instances number has been atteined or if the underlying
* max instances number has been attained or if the underlying
* implementation does not support setting details of the corresponding
* class.
* @throws OperationFailedException with code Network Failure if putting the
* new value online has failed
* @throws java.lang.ArrayIndexOutOfBoundsException if the number of
* instances currently registered by the application is already equal to the
* maximum number of supported instances (@see getMaxDetailInstances())
@ -154,15 +164,13 @@ public void addDetail(ServerStoredDetails.GenericDetail detail)
ArrayIndexOutOfBoundsException;
/**
* Removes the specified detail from the list of details stored online for
* this account. The method returns a boolean indicating if such a detail
* was found (and removed) or not.
* Removes the specified detail from the list of details ready to be saved
* online this account. The method returns a boolean indicating if such a
* detail was found (and removed) or not.
* <p>
* @param detail the detail to remove
* @return true if the specified detail existed and was successfully removed
* and false otherwise.
* @throws OperationFailedException with code Network Failure if removing the
* detail from the server has failed
*/
public boolean removeDetail(ServerStoredDetails.GenericDetail detail)
throws OperationFailedException;
@ -186,6 +194,17 @@ public boolean replaceDetail(
ServerStoredDetails.GenericDetail newDetailValue)
throws ClassCastException, OperationFailedException;
/**
* Saves the list of details for this account that were ready to be stored
* online on the server. This method performs the actual saving of details
* online on the server and is supposed to be invoked after addDetail(),
* replaceDetail() and/or removeDetail().
* <p>
* @throws OperationFailedException with code Network Failure if putting the
* new values back online has failed.
*/
public void save() throws OperationFailedException;
/**
* Registers a ServerStoredDetailsChangeListener with this operation set so
* that it gets notifications of details change.

@ -306,6 +306,12 @@ public CountryDetail(Locale locale)
{
super("Country", locale);
}
public CountryDetail(String country)
{
super("Country", null);
value = country;
}
}
/**
@ -317,6 +323,11 @@ public WorkCountryDetail(Locale locale)
{
super(locale);
}
public WorkCountryDetail(String country)
{
super(country);
}
}
//-------------------------------- Language ------------------------------------
@ -447,6 +458,43 @@ public URL getURL()
{
return (URL)getDetailValue();
}
/**
* Compares two URLDetails according their name
* and URLs
*
* @param obj Object expected URLDetail otherwise return false
* @return <tt>true</tt> if this object has the same name and
* URL value as <tt>obj</tt> and false otherwise
*/
@Override
public boolean equals(Object obj)
{
if (!(obj instanceof URLDetail))
return false;
if (this == obj)
{
return true;
}
URLDetail other = (URLDetail)obj;
boolean equalsDisplayName =
this.detailDisplayName != null
&& other.getDetailDisplayName() != null
&& this.detailDisplayName.equals(other.getDetailDisplayName());
boolean equalValues =
this.value != null
&& other.getDetailValue() != null
&& this.value.equals(other.getDetailValue());
boolean bothNullValues =
this.value == null && other.value == null;
if (equalsDisplayName && (equalValues || bothNullValues))
return true;
else
return false;
}
}
/**
@ -487,6 +535,44 @@ public byte[] getBytes()
{
return (byte[])getDetailValue();
}
/**
* Compares two BinaryDetails according their DetailDisplayName
* and the result of invoking their getBytes() methods.
*
* @param obj Object expected BinaryDetail otherwise return false
* @return <tt>true</tt> if this object has the same display name and
* value as <tt>obj</tt> and false otherwise
*/
@Override
public boolean equals(Object obj)
{
if (!(obj instanceof BinaryDetail))
return false;
if (this == obj)
{
return true;
}
BinaryDetail other = (BinaryDetail)obj;
boolean equalsDisplayName =
this.detailDisplayName != null
&& other.getDetailDisplayName() != null
&& this.detailDisplayName.equals(other.getDetailDisplayName());
boolean equalsNotNull =
this.value != null
&& other.getDetailValue() != null
&& Arrays.equals(this.getBytes(), other.getBytes());
boolean nullOrEmpty =
(this.value == null || this.getBytes().length == 0)
&& (other.getDetailValue() == null
|| other.getBytes().length == 0);
if (equalsDisplayName && (equalsNotNull || nullOrEmpty))
return true;
else
return false;
}
}
/**
@ -641,6 +727,48 @@ public BirthDateDetail(Calendar date)
{
super("Birth Date", date);
}
/**
* Compares two BirthDateDetails according to their
* Calender's year, month and day.
*
* @param obj Object expected BirthDateDetail otherwise return false
* @return <tt>true</tt> if this object has the same value as
* <tt>obj</tt> and false otherwise
*/
@Override
public boolean equals(Object obj)
{
if(!(obj instanceof BirthDateDetail))
return false;
if(this == obj)
{
return true;
}
BirthDateDetail other = (BirthDateDetail)obj;
// both null dates
if (this.value == null && other.getDetailValue() == null)
return true;
if (this.value != null && other.getDetailValue() != null)
{
boolean yearEquals =
((Calendar)this.value).get(Calendar.YEAR) ==
((Calendar)other.value).get(Calendar.YEAR);
boolean monthEquals =
((Calendar)this.value).get(Calendar.MONTH) ==
((Calendar)other.value).get(Calendar.MONTH);
boolean dayEquals =
((Calendar)this.value).get(Calendar.DAY_OF_MONTH) ==
((Calendar)other.value).get(Calendar.DAY_OF_MONTH);
return yearEquals && monthEquals && dayEquals;
}
else
return false;
}
}
/**
@ -747,4 +875,27 @@ public boolean getBoolean()
return ((Boolean)getDetailValue()).booleanValue();
}
}
//---------------------------- Others ------------------------------------------
/**
* A job title.
*/
public static class JobTitleDetail extends StringDetail
{
public JobTitleDetail(String jobTitle)
{
super("Job Title", jobTitle);
}
}
/**
* Represents a (personal) "about me" short description.
*/
public static class AboutMeDetail extends StringDetail
{
public AboutMeDetail(String description)
{
super("Description", description);
}
}
}

Loading…
Cancel
Save