diff --git a/build.xml b/build.xml index 6778445bf..5b355e152 100644 --- a/build.xml +++ b/build.xml @@ -618,6 +618,7 @@ bundle-ssh,bundle-plugin-sshaccregwizz, bundle-contacteventhandler,bundle-plugin-contactinfo, bundle-plugin-accountinfo,bundle-plugin-chatalerter, + bundle-plugin-statusupdate, bundle-updatecheckplugin"/> @@ -1615,7 +1616,7 @@ javax.swing.event, javax.swing.border"/> - @@ -1624,4 +1625,15 @@ javax.swing.event, javax.swing.border"/> + + + + + + + + diff --git a/lib/felix.client.run.properties b/lib/felix.client.run.properties index f641a250b..36b1ad4e7 100644 --- a/lib/felix.client.run.properties +++ b/lib/felix.client.run.properties @@ -115,8 +115,9 @@ felix.auto.start.60= \ reference:file:sc-bundles/contactinfo.jar \ reference:file:sc-bundles/accountinfo.jar \ reference:file:sc-bundles/chatalerter.jar \ - reference:file:sc-bundles/shutdown.jar - + reference:file:sc-bundles/shutdown.jar \ + reference:file:sc-bundles/statusupdate.jar \ + # Uncomment the following lines if you want to run the architect viewer # bundle. #oscar.auto.start.100= \ diff --git a/resources/languages/plugin/statusupdate/resources.properties b/resources/languages/plugin/statusupdate/resources.properties new file mode 100644 index 000000000..f899ca6bc --- /dev/null +++ b/resources/languages/plugin/statusupdate/resources.properties @@ -0,0 +1,7 @@ +cancel=Cancel +enable=Enable +info=Information +infotext=Set "Idle", when your computer is not used for X minutes. The status is reset to the status before, when activity is detected. +menuEntry=Automatic Status +minutes=Minutes +ok=OK diff --git a/resources/languages/plugin/statusupdate/resources_de.properties b/resources/languages/plugin/statusupdate/resources_de.properties new file mode 100644 index 000000000..2a3687c8d --- /dev/null +++ b/resources/languages/plugin/statusupdate/resources_de.properties @@ -0,0 +1,7 @@ +cancel=Abbrechen +enable=Aktivieren +info=Information +infotext=Setze "Abwesend" als Status, wenn der Computer für X Minuten nicht verwendet wird. Der Status wird bei auf den vorangegangen Status zurückgesetzt, sobald der Computer wieder aktiv verwendet wird. +menuEntry=Automatischer Status +minutes=Minuten +ok=OK diff --git a/src/net/java/sip/communicator/plugin/statusupdate/ConfigurationDialog.java b/src/net/java/sip/communicator/plugin/statusupdate/ConfigurationDialog.java new file mode 100644 index 000000000..0f0df2551 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/statusupdate/ConfigurationDialog.java @@ -0,0 +1,194 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.statusupdate; + +import java.awt.*; +import java.awt.event.*; +import java.util.Vector; + +import javax.swing.*; + +import net.java.sip.communicator.service.configuration.ConfigurationService; + +/** + * The configuration Dialog for the Mail Notification Plugin + * + * @author Thomas Hofer + * + */ +public class ConfigurationDialog extends JDialog +{ + /** + * + */ + private static final long serialVersionUID = -3850044618335728627L; + + private JCheckBox enable; + private JSpinner timer; + + /** + * Default Constructor + */ + public ConfigurationDialog() + { + super(); + init(); + initValues(); + + getContentPane().setPreferredSize(new Dimension(400, 200)); + getContentPane().setLayout(new GridLayout(1, 1)); + // move window to middle of screen + setLocation( + (Toolkit.getDefaultToolkit().getScreenSize().width - getPreferredSize().width) / 2, + (Toolkit.getDefaultToolkit().getScreenSize().height - getPreferredSize().height) / 2); + } + + /** + * Initialize the ui-components + */ + private void init() + { + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowAdapter() + { + public void windowClosing(WindowEvent e) + { + } + }); + + final ConfigurationService configService = StatusUpdateActivator + .getConfigService(); + + JPanel mainPanel = new JPanel(); + mainPanel.setForeground(Color.GRAY); + mainPanel.setLayout(new GridBagLayout()); + + JTextArea infoLabel = new JTextArea(Resources.getString("infotext")); + infoLabel.setBorder(BorderFactory.createTitledBorder(Resources + .getString("info"))); + infoLabel.setEditable(false); + infoLabel.setWrapStyleWord(true); + infoLabel.setLineWrap(true); + + enable = new JCheckBox(Resources.getString("enable")); + enable.addActionListener(new ActionListener() + { + + public void actionPerformed(ActionEvent e) + { + timer.setEnabled(enable.isSelected()); + } + }); + + timer = new JSpinner(new SpinnerNumberModel(15, 1, 180, 1)); + + GridBagConstraints c = new GridBagConstraints(); + c.gridy = 0; + c.weightx = 1; + c.fill = GridBagConstraints.HORIZONTAL; + c.anchor = GridBagConstraints.NORTHWEST; + + JPanel okCancelPanel = new JPanel(); + JButton ok = new JButton(Resources.getString("ok")); + ok.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + configService.setProperty(Preferences.ENABLE, Boolean + .toString(enable.isSelected())); + Integer interval = (Integer) timer.getValue(); + configService.setProperty(Preferences.TIMER, interval); + setVisible(false); + } + }); + JButton cancel = new JButton(Resources.getString("cancel")); + cancel.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + setVisible(false); + } + }); + + okCancelPanel.add(ok); + okCancelPanel.add(cancel); + okCancelPanel.setBorder(BorderFactory.createTitledBorder(" ")); + + GridBagConstraints mainGBC = new GridBagConstraints(); + mainGBC.gridx = 0; + mainGBC.gridy = 0; + mainGBC.weightx = 1; + mainGBC.fill = GridBagConstraints.BOTH; + mainGBC.anchor = GridBagConstraints.NORTHWEST; + mainGBC.weighty = 1; + mainGBC.gridwidth = 3; + + mainPanel.add(infoLabel, mainGBC); + + mainGBC.fill = GridBagConstraints.HORIZONTAL; + mainGBC.gridwidth = 1; + mainGBC.gridy++; + mainGBC.weightx = 1; + mainGBC.weighty = 0; + mainGBC.gridx = 0; + mainPanel.add(enable, mainGBC); + + mainGBC.weightx = 0; + mainGBC.gridx++; + mainPanel.add(timer, mainGBC); + mainGBC.weightx = 1; + mainGBC.gridx++; + mainPanel.add(new JLabel(Resources.getString("minutes")), mainGBC); + + mainGBC.gridwidth = 3; + mainGBC.gridx = 0; + mainGBC.gridy++; + mainGBC.weighty = 0; + mainPanel.add(okCancelPanel, mainGBC); + + this.getContentPane().add(mainPanel); + } + + /** + * (Re-)Initializes the values of the settings dependent on the selected + * account + */ + private void initValues() + { + ConfigurationService configService = StatusUpdateActivator + .getConfigService(); + + String e = (String) configService.getProperty(Preferences.ENABLE); + if (e != null) + { + try + { + enable.setSelected(Boolean.parseBoolean(e)); + timer.setEnabled(Boolean.parseBoolean(e)); + } catch (NumberFormatException ex) + { + enable.setSelected(false); + timer.setEnabled(false); + } + } else + { + enable.setSelected(false); + timer.setEnabled(false); + } + + String t = (String) configService.getString(Preferences.TIMER); + if (t != null) + { + try + { + timer.setValue(Integer.parseInt(t)); + } catch (NumberFormatException ex) + { + } + } + } +} diff --git a/src/net/java/sip/communicator/plugin/statusupdate/Preferences.java b/src/net/java/sip/communicator/plugin/statusupdate/Preferences.java new file mode 100644 index 000000000..e51dc741e --- /dev/null +++ b/src/net/java/sip/communicator/plugin/statusupdate/Preferences.java @@ -0,0 +1,19 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.statusupdate; + +/** + * Preferences for the Status Update + * + * @author Thomas Hofer + * + */ +public final class Preferences +{ + public static final String ENABLE = "at.liwest.communicator.plugin.statusupdate.enable"; + public static final String TIMER = "at.liwest.communicator.plugin.statusupdate.timer"; +} diff --git a/src/net/java/sip/communicator/plugin/statusupdate/Resources.java b/src/net/java/sip/communicator/plugin/statusupdate/Resources.java new file mode 100644 index 000000000..119cceeb8 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/statusupdate/Resources.java @@ -0,0 +1,44 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.statusupdate; + +import java.util.*; + +/** + * The Messages class manages the access to the internationalization properties + * files. + * + * @author Thomas Hofer; + */ +public class Resources +{ + + private static final String BUNDLE_NAME = "resources.languages.plugin.statusupdate.resources"; + + private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle + .getBundle(BUNDLE_NAME); + + /** + * Returns an internationalized string corresponding to the given key. + * + * @param key + * The key of the string. + * @return An internationalized string corresponding to the given key. + */ + public static String getString(String key) + { + try + { + return RESOURCE_BUNDLE.getString(key); + + } catch (MissingResourceException e) + { + + return '!' + key + '!'; + } + } +} diff --git a/src/net/java/sip/communicator/plugin/statusupdate/SettingsWindowMenuEntry.java b/src/net/java/sip/communicator/plugin/statusupdate/SettingsWindowMenuEntry.java new file mode 100644 index 000000000..1f1c3af20 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/statusupdate/SettingsWindowMenuEntry.java @@ -0,0 +1,63 @@ +package net.java.sip.communicator.plugin.statusupdate; + +import java.awt.Dialog.*; +import java.awt.event.*; + +import javax.swing.JMenuItem; + +import net.java.sip.communicator.service.contactlist.*; +import net.java.sip.communicator.service.gui.*; + +public class SettingsWindowMenuEntry implements PluginComponent +{ + + private JMenuItem settingsMenuEntry = new JMenuItem(Resources + .getString("menuEntry")); + + private Container container; + + public SettingsWindowMenuEntry(Container container) + { + settingsMenuEntry.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + ConfigurationDialog dialog = new ConfigurationDialog(); + dialog.pack(); + dialog.setModalityType(ModalityType.APPLICATION_MODAL); + dialog.setVisible(true); + + StatusUpdateActivator.startThread(); + } + }); + this.container = container; + } + + public Object getComponent() + { + return settingsMenuEntry; + } + + public String getConstraints() + { + return null; + } + + public Container getContainer() + { + return container; + } + + public String getName() + { + return Resources.getString("aboutMenuEntry"); + } + + public void setCurrentContact(MetaContact metaContact) + { + } + + public void setCurrentContactGroup(MetaContactGroup metaGroup) + { + } +} diff --git a/src/net/java/sip/communicator/plugin/statusupdate/StatusUpdateActivator.java b/src/net/java/sip/communicator/plugin/statusupdate/StatusUpdateActivator.java new file mode 100644 index 000000000..abc51faa2 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/statusupdate/StatusUpdateActivator.java @@ -0,0 +1,196 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.statusupdate; + +import java.util.*; + +import net.java.sip.communicator.service.configuration.ConfigurationService; +import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.service.protocol.ProtocolProviderService; +import net.java.sip.communicator.util.Logger; + +import org.osgi.framework.*; + +/** + * Activator of the StatusUpdate Bundle + * + * @author Thomas Hofer + */ +public class StatusUpdateActivator implements BundleActivator +{ + + private static Logger logger = Logger + .getLogger(StatusUpdateActivator.class); + + private static BundleContext bundleContext = null; + + private static Thread thread = null; + private static StatusUpdateThread runner = null; + + private ServiceRegistration menuRegistration; + + /** + * Starts this bundle + * + * @param bundleContext + * BundleContext + * @throws Exception + */ + public void start(BundleContext bc) throws Exception + { + bundleContext = bc; + new Thread(new Runnable() + { + + public void run() + { + try + { + Thread.sleep(5000); + } catch (InterruptedException e) + { + } + // wait a few seconds + startThread(); + } + }).start(); + registerMenuEntry(); + } + + static void startThread() + { + ConfigurationService configService = getConfigService(); + String e = (String) configService.getProperty(Preferences.ENABLE); + if (e == null) + { + return; + } + try + { + boolean enabled = Boolean.parseBoolean(e); + if (!enabled) + { + return; + } + } catch (NumberFormatException ex) + { + return; + } + + if (runner == null) + { + runner = new StatusUpdateThread(); + } + if (thread == null || !runner.isRunning()) + { + thread = new Thread(runner); + thread.setName(StatusUpdateActivator.class.getName()); + thread.setPriority(Thread.MIN_PRIORITY); + thread.setDaemon(true); + thread.start(); + } else + { + thread.interrupt(); + } + } + + /** + * stop the bundle + */ + public void stop(BundleContext bundleContext) throws Exception + { + stopThread(); + unRegisterMenuEntry(); + } + + static void stopThread() + { + if (runner != null) + { + runner.stop(); + runner = null; + } + if (thread != null) + { + thread.interrupt(); + thread = null; + } + } + + static ProtocolProviderService[] getProtocolProviders() + { + // get the protocol provider factory + BundleContext bundleContext = StatusUpdateActivator.bundleContext; + + ServiceReference[] serRefs = null; + // String osgiFilter = "(" + ProtocolProviderFactory.PROTOCOL + "=" + // + ProtocolNames.SIP + ")"; + + try + { + // serRefs = bundleContext.getServiceReferences( + // ProtocolProviderFactory.class.getName(), osgiFilter); + serRefs = bundleContext.getAllServiceReferences( + ProtocolProviderService.class.getName(), null); + } catch (InvalidSyntaxException ex) + { + logger.error(ex); + } + + if (serRefs == null || serRefs[0] == null) + { + return null; + } + + Set pps = new HashSet(); + + for (ServiceReference serviceReference : serRefs) + { + ProtocolProviderService protocolProvider = (ProtocolProviderService) bundleContext + .getService(serviceReference); + pps.add(protocolProvider); + } + + return pps.toArray(new ProtocolProviderService[0]); + } + + /** + * Gets the ConfigurationService + * + * @return + */ + static ConfigurationService getConfigService() + { + // retrieve a reference to the config access service. + ServiceReference confServiceRefs = bundleContext + .getServiceReference(ConfigurationService.class.getName()); + + return (ConfigurationService) bundleContext.getService(confServiceRefs); + } + + private void registerMenuEntry() + { + SettingsWindowMenuEntry menuEntry = new SettingsWindowMenuEntry( + Container.CONTAINER_TOOLS_MENU); + + Hashtable toolsMenuFilter = new Hashtable(); + toolsMenuFilter.put(Container.CONTAINER_ID, + Container.CONTAINER_TOOLS_MENU.getID()); + + menuRegistration = bundleContext.registerService(PluginComponent.class + .getName(), menuEntry, toolsMenuFilter); + } + + private void unRegisterMenuEntry() + { + if (menuRegistration != null) + { + menuRegistration.unregister(); + } + } + +} \ No newline at end of file diff --git a/src/net/java/sip/communicator/plugin/statusupdate/StatusUpdateThread.java b/src/net/java/sip/communicator/plugin/statusupdate/StatusUpdateThread.java new file mode 100644 index 000000000..26f5fa1f1 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/statusupdate/StatusUpdateThread.java @@ -0,0 +1,217 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.statusupdate; + +import java.awt.*; +import java.util.*; + +import net.java.sip.communicator.service.configuration.ConfigurationService; +import net.java.sip.communicator.service.protocol.*; + +/** + * A Runnable, which permanently looks at the mouse position. If the mouse is + * not moved, all accounts are set to "Away" or similar states. + * + * @author Thomas Hofer + * + */ +public class StatusUpdateThread implements Runnable +{ + private boolean run = false; + private Point lastPosition = null; + private Map lastStates = new HashMap(); + + private static final int IDLE_TIMER = 3000; + private static final int AWAY_DEFAULT_STATUS = 40; + + public void run() + { + run = true; + int timer = 0; + do + { + try + { + Point currentPosition = MouseInfo.getPointerInfo() + .getLocation(); + if (!isNear(lastPosition, currentPosition)) + { + // position has changed + // check, if a minor state has been automatically set and + // reset this state to the former state. + ProtocolProviderService[] pps = StatusUpdateActivator + .getProtocolProviders(); + + for (ProtocolProviderService protocolProviderService : pps) + { + if (lastStates.get(protocolProviderService) != null) + { + PresenceStatus lastState = lastStates + .get(protocolProviderService); + OperationSetPresence presence = (OperationSetPresence) protocolProviderService + .getOperationSet(OperationSetPresence.class); + try + { + presence.publishPresenceStatus(lastState, ""); + } catch (IllegalArgumentException e) + { + } catch (IllegalStateException e) + { + } catch (OperationFailedException e) + { + } + lastStates.remove(protocolProviderService); + } + } + timer = getTimer() * 1000 * 60; + } else + { + // position has not changed! + // get all protocols and set them to away + + ProtocolProviderService[] pps = StatusUpdateActivator + .getProtocolProviders(); + + for (ProtocolProviderService protocolProviderService : pps) + { + OperationSetPresence presence = (OperationSetPresence) protocolProviderService + .getOperationSet(OperationSetPresence.class); + + PresenceStatus status = presence.getPresenceStatus(); + + if (status.getStatus() < PresenceStatus.AVAILABLE_THRESHOLD) + { + // already (manually) set to away or lower + break; + } + + lastStates.put(protocolProviderService, presence + .getPresenceStatus()); + + PresenceStatus newStatus = findAwayStatus(presence); + + try + { + presence.publishPresenceStatus(newStatus, newStatus + .getStatusName()); + } catch (IllegalArgumentException e) + { + } catch (IllegalStateException e) + { + } catch (OperationFailedException e) + { + } + } + + timer = IDLE_TIMER; + } + lastPosition = currentPosition; + Thread.sleep(timer); + } catch (InterruptedException e) + { + } + } while (run && timer > 0); + } + + public void stop() + { + run = false; + } + + /** + * Finds the Away-Status of the protocols + * + * @param presence + * @return + */ + private PresenceStatus findAwayStatus(OperationSetPresence presence) + { + Iterator statusSet = presence.getSupportedStatusSet(); + + PresenceStatus status = null; + + while (statusSet.hasNext()) + { + PresenceStatus possibleState = (PresenceStatus) statusSet.next(); + + if (possibleState.getStatus() < PresenceStatus.AVAILABLE_THRESHOLD + && possibleState.getStatus() >= PresenceStatus.ONLINE_THRESHOLD) + { + if (status == null + || (Math.abs(possibleState.getStatus() + - AWAY_DEFAULT_STATUS) < Math.abs(status + .getStatus() + - AWAY_DEFAULT_STATUS))) + { + status = possibleState; + } + } + } + return status; + } + + private int getTimer() + { + ConfigurationService configService = StatusUpdateActivator + .getConfigService(); + + String e = (String) configService.getProperty(Preferences.ENABLE); + if (e == null) + { + return 0; + } + try + { + boolean enabled = Boolean.parseBoolean(e); + if (!enabled) + { + return 0; + } + } catch (NumberFormatException ex) + { + return 0; + } + + String t = (String) configService.getString(Preferences.TIMER); + int timer = 0; + try + { + timer = Integer.parseInt(t); + } catch (NumberFormatException ex) + { + return 0; + } + return timer; + } + + public boolean isRunning() + { + return run; + } + + private boolean isNear(Point p1, Point p2) + { + if (p1 == null) + { + return false; + } + if (p2 == null) + { + return false; + } + if (Math.abs(p1.x - p2.x) > 10) + { + return false; + } + if (Math.abs(p1.y - p2.y) > 10) + { + return false; + } + return true; + + } +} diff --git a/src/net/java/sip/communicator/plugin/statusupdate/statusupdate.manifest.mf b/src/net/java/sip/communicator/plugin/statusupdate/statusupdate.manifest.mf new file mode 100644 index 000000000..cf9852f8f --- /dev/null +++ b/src/net/java/sip/communicator/plugin/statusupdate/statusupdate.manifest.mf @@ -0,0 +1,15 @@ +Bundle-Activator: net.java.sip.communicator.plugin.statusupdate.StatusUpdateActivator +Bundle-Name: StatusUpdatePlugin +Bundle-Description: A bundle that automatically can update the status of your protocols +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.2 +Import-Package: org.osgi.framework, + net.java.sip.communicator.util, + net.java.sip.communicator.service.protocol, + net.java.sip.communicator.service.configuration, + net.java.sip.communicator.service.gui, + javax.swing, + javax.swing.border, + + + \ No newline at end of file