aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryanas <yana@jitsi.org>2013-11-25 17:38:14 +0100
committeryanas <yana@jitsi.org>2013-11-25 17:38:14 +0100
commit9383a5e000aa64c308892dc71fcefd3ab7a2d542 (patch)
tree2bd8a34d74130c265da36497e23c63c2daafe270
parent25f32080449feb8acc763099780f42dad3041b83 (diff)
downloadjitsi-9383a5e000aa64c308892dc71fcefd3ab7a2d542.zip
jitsi-9383a5e000aa64c308892dc71fcefd3ab7a2d542.tar.gz
jitsi-9383a5e000aa64c308892dc71fcefd3ab7a2d542.tar.bz2
Added improvements to OTR user interface, among which new icons and
tooltips. Patch provided by Marin Dzhigarov on Nov 25, 2013.
-rw-r--r--resources/images/images.properties4
-rw-r--r--resources/images/plugin/otr/encrypted_verified22x22.pngbin0 -> 1561 bytes
-rw-r--r--resources/images/plugin/otr/padlockBrokenOpen.pngbin0 -> 1572 bytes
-rw-r--r--resources/images/plugin/otr/padlockLoading.gifbin0 -> 419 bytes
-rw-r--r--resources/images/plugin/otr/plaintext22x22.pngbin408 -> 1456 bytes
-rw-r--r--resources/languages/resources.properties5
-rw-r--r--src/net/java/sip/communicator/plugin/otr/OtrContactMenu.java171
-rw-r--r--src/net/java/sip/communicator/plugin/otr/OtrMetaContactButton.java159
-rw-r--r--src/net/java/sip/communicator/plugin/otr/OtrTransformLayer.java16
-rw-r--r--src/net/java/sip/communicator/plugin/otr/OtrWeakListener.java155
-rw-r--r--src/net/java/sip/communicator/plugin/otr/ScOtrEngine.java9
-rw-r--r--src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java114
-rw-r--r--src/net/java/sip/communicator/plugin/otr/ScSessionStatus.java29
13 files changed, 424 insertions, 238 deletions
diff --git a/resources/images/images.properties b/resources/images/images.properties
index 525cd0c..3ca43da 100644
--- a/resources/images/images.properties
+++ b/resources/images/images.properties
@@ -543,13 +543,15 @@ plugin.securityconfig.ICON=resources/images/plugin/securityconfig/security.png
# otr plugin icons
plugin.otr.ENCRYPTED_ICON_16x16=resources/images/plugin/otr/encrypted16x16.png
-plugin.otr.ENCRYPTED_ICON_22x22=resources/images/plugin/otr/encrypted22x22.png
+plugin.otr.ENCRYPTED_ICON_22x22=resources/images/plugin/otr/encrypted_verified22x22.png
plugin.otr.ENCRYPTED_UNVERIFIED_ICON_16x16=resources/images/plugin/otr/encrypted_unverified16x16.png
plugin.otr.ENCRYPTED_UNVERIFIED_ICON_22x22=resources/images/plugin/otr/encrypted_unverified22x22.png
plugin.otr.PLAINTEXT_ICON_16x16=resources/images/plugin/otr/plaintext16x16.png
plugin.otr.PLAINTEXT_ICON_22x22=resources/images/plugin/otr/plaintext22x22.png
plugin.otr.FINISHED_ICON_16x16=resources/images/plugin/otr/finished16x16.png
plugin.otr.FINISHED_ICON_22x22=resources/images/plugin/otr/finished22x22.png
+plugin.otr.LOADING_ICON_22x22=resources/images/plugin/otr/padlockLoading.gif
+plugin.otr.BROKEN_ICON_22x22=resources/images/plugin/otr/padlockBrokenOpen.png
plugin.otr.HELP_ICON_15x15=resources/images/plugin/otr/help15x15.png
plugin.otr.MENU_ITEM_ICON_16x16=resources/images/plugin/otr/otr_menu_icon.png
diff --git a/resources/images/plugin/otr/encrypted_verified22x22.png b/resources/images/plugin/otr/encrypted_verified22x22.png
new file mode 100644
index 0000000..22bd877
--- /dev/null
+++ b/resources/images/plugin/otr/encrypted_verified22x22.png
Binary files differ
diff --git a/resources/images/plugin/otr/padlockBrokenOpen.png b/resources/images/plugin/otr/padlockBrokenOpen.png
new file mode 100644
index 0000000..41028b7
--- /dev/null
+++ b/resources/images/plugin/otr/padlockBrokenOpen.png
Binary files differ
diff --git a/resources/images/plugin/otr/padlockLoading.gif b/resources/images/plugin/otr/padlockLoading.gif
new file mode 100644
index 0000000..4aeacde
--- /dev/null
+++ b/resources/images/plugin/otr/padlockLoading.gif
Binary files differ
diff --git a/resources/images/plugin/otr/plaintext22x22.png b/resources/images/plugin/otr/plaintext22x22.png
index bc298e5..912525e 100644
--- a/resources/images/plugin/otr/plaintext22x22.png
+++ b/resources/images/plugin/otr/plaintext22x22.png
Binary files differ
diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties
index 80b712c..1cc64b9 100644
--- a/resources/languages/resources.properties
+++ b/resources/languages/resources.properties
@@ -1531,6 +1531,11 @@ plugin.securityconfig.masterpassword.MP_INPUT=Please enter your master password:
plugin.otr.menu.TITLE=Secure chat
plugin.otr.menu.START_OTR=Start private conversation
plugin.otr.menu.END_OTR=End private conversation
+plugin.otr.menu.FINISHED=Your buddy has ended your private conversation. You should do the same
+plugin.otr.menu.VERIFIED=Your private conversation is verified
+plugin.otr.menu.UNVERIFIED=Your private conversation is not verified. Please authenticate your buddy
+plugin.otr.menu.LOADING_OTR=Starting private conversation...
+plugin.otr.menu.TIMED_OUT=Starting private conversation timed out.
plugin.otr.menu.REFRESH_OTR=Refresh private conversation
plugin.otr.menu.AUTHENTICATE_BUDDY=Authenticate buddy
plugin.otr.menu.WHATS_THIS=What's this
diff --git a/src/net/java/sip/communicator/plugin/otr/OtrContactMenu.java b/src/net/java/sip/communicator/plugin/otr/OtrContactMenu.java
index 1dd402f..040f932 100644
--- a/src/net/java/sip/communicator/plugin/otr/OtrContactMenu.java
+++ b/src/net/java/sip/communicator/plugin/otr/OtrContactMenu.java
@@ -7,12 +7,10 @@
package net.java.sip.communicator.plugin.otr;
import java.awt.event.*;
-import java.lang.ref.*;
import javax.swing.*;
import net.java.otr4j.*;
-import net.java.otr4j.session.*;
import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.service.protocol.*;
@@ -62,7 +60,7 @@ class OtrContactMenu
*/
private OtrPolicy otrPolicy;
- private SessionStatus sessionStatus;
+ private ScSessionStatus sessionStatus;
private final JMenu parentMenu;
@@ -94,14 +92,14 @@ class OtrContactMenu
* XXX This OtrContactMenu instance cannot be added as a listener to
* scOtrEngine and scOtrKeyManager without being removed later on
* because the latter live forever. Unfortunately, the dispose() method
- * of this instance is never executed. WeakListener will keep this
+ * of this instance is never executed. OtrWeakListener will keep this
* instance as a listener of scOtrEngine and scOtrKeyManager for as long
* as this instance is necessary. And this instance will be strongly
* referenced by the JMenuItems which depict it. So when the JMenuItems
- * are gone, this instance will become obsolete and WeakListener will
+ * are gone, this instance will become obsolete and OtrWeakListener will
* remove it as a listener of scOtrEngine and scOtrKeyManager.
*/
- new WeakListener(
+ new OtrWeakListener<OtrContactMenu>(
this,
OtrActivator.scOtrEngine, OtrActivator.scOtrKeyManager);
@@ -258,6 +256,19 @@ class OtrContactMenu
switch (this.sessionStatus)
{
+ case LOADING:
+ if (separateMenu != null)
+ {
+ separateMenu.add(endOtr);
+ separateMenu.add(refreshOtr);
+ }
+ else
+ {
+ parentMenu.add(endOtr);
+ parentMenu.add(refreshOtr);
+ }
+ break;
+
case ENCRYPTED:
JMenuItem authBuddy = new JMenuItem();
authBuddy.setText(OtrActivator.resourceService
@@ -293,6 +304,7 @@ class OtrContactMenu
}
break;
+ case TIMED_OUT:
case PLAINTEXT:
if (separateMenu != null)
separateMenu.add(startOtr);
@@ -406,9 +418,9 @@ class OtrContactMenu
* icon and, if necessary, rebuilds the menuitems to match the passed in
* sessionStatus.
*
- * @param sessionStatus the {@link SessionStatus}.
+ * @param sessionStatus the {@link ScSessionStatus}.
*/
- private void setSessionStatus(SessionStatus sessionStatus)
+ private void setSessionStatus(ScSessionStatus sessionStatus)
{
if (sessionStatus != this.sessionStatus)
{
@@ -478,147 +490,4 @@ class OtrContactMenu
separateMenu.setIcon(OtrActivator.resourceService.getImage(imageID));
}
-
- /**
- * Implements a <tt>ScOtrEngineListener</tt> and
- * <tt>ScOtrKeyManagerListener</tt> listener for the purposes of
- * <tt>OtrContactMenu</tt> which listens to <tt>ScOtrEngine</tt> and
- * <tt>ScOtrKeyManager</tt> while weakly referencing the
- * <tt>OtrContactMenu</tt>. Fixes a memory leak of <tt>OtrContactMenu</tt>
- * instances because these cannot determine when they are to be explicitly
- * disposed.
- *
- * @author Lyubomir Marinov
- */
- private static class WeakListener
- implements ScOtrEngineListener,
- ScOtrKeyManagerListener
- {
- /**
- * The <tt>ScOtrEngine</tt> the <tt>OtrContactMenu</tt> associated with
- * this instance is to listen to.
- */
- private final ScOtrEngine engine;
-
- /**
- * The <tt>ScOtrKeyManager</tt> the <tt>OtrContactMenu</tt> associated
- * with this instance is to listen to.
- */
- private final ScOtrKeyManager keyManager;
-
- /**
- * The <tt>OtrContactMenu</tt> which is associated with this instance
- * and which is to listen to {@link #engine} and {@link #keyManager}.
- */
- private final WeakReference<OtrContactMenu> listener;
-
- /**
- * Initializes a new <tt>WeakListener</tt> instance which is to allow
- * a specific <tt>OtrContactMenu</tt> to listener to a specific
- * <tt>ScOtrEngine</tt> and a specific <tt>ScOtrKeyManager</tt> without
- * being retained by them forever (because they live forever).
- *
- * @param listener the <tt>OtrContactMenu</tt> which is to listen to the
- * specified <tt>engine</tt> and <tt>keyManager</tt>
- * @param engine the <tt>ScOtrEngine</tt> which is to be listened to by
- * the specified <tt>OtrContactMenu</tt>
- * @param keyManager the <tt>ScOtrKeyManager</tt> which is to be
- * listened to by the specified <tt>OtrContactMenu</tt>
- */
- public WeakListener(
- OtrContactMenu listener,
- ScOtrEngine engine, ScOtrKeyManager keyManager)
- {
- if (listener == null)
- throw new NullPointerException("listener");
-
- this.listener = new WeakReference<OtrContactMenu>(listener);
- this.engine = engine;
- this.keyManager = keyManager;
-
- this.engine.addListener(this);
- this.keyManager.addListener(this);
- }
-
- /**
- * {@inheritDoc}
- *
- * Forwards the event/notification to the associated
- * <tt>OtrContactMenu</tt> if it is still needed by the application.
- */
- public void contactPolicyChanged(Contact contact)
- {
- ScOtrEngineListener l = getListener();
-
- if (l != null)
- l.contactPolicyChanged(contact);
- }
-
- /**
- * {@inheritDoc}
- *
- * Forwards the event/notification to the associated
- * <tt>OtrContactMenu</tt> if it is still needed by the application.
- */
- public void contactVerificationStatusChanged(Contact contact)
- {
- ScOtrKeyManagerListener l = getListener();
-
- if (l != null)
- l.contactVerificationStatusChanged(contact);
- }
-
- /**
- * Gets the <tt>OtrContactMenu</tt> which is listening to
- * {@link #engine} and {@link #keyManager}. If the
- * <tt>OtrContactMenu</tt> is no longer needed by the application, this
- * instance seizes listening to <tt>engine</tt> and <tt>keyManager</tt>
- * and allows the memory used by this instance to be reclaimed by the
- * Java virtual machine.
- *
- * @return the <tt>OtrContactMenu</tt> which is listening to
- * <tt>engine</tt> and <tt>keyManager</tt> if it is still needed by the
- * application; otherwise, <tt>null</tt>
- */
- private OtrContactMenu getListener()
- {
- OtrContactMenu l = this.listener.get();
-
- if (l == null)
- {
- engine.removeListener(this);
- keyManager.removeListener(this);
- }
-
- return l;
- }
-
- /**
- * {@inheritDoc}
- *
- * Forwards the event/notification to the associated
- * <tt>OtrContactMenu</tt> if it is still needed by the application.
- */
- public void globalPolicyChanged()
- {
- ScOtrEngineListener l = getListener();
-
- if (l != null)
- l.globalPolicyChanged();
- }
-
- /**
- * {@inheritDoc}
- *
- * Forwards the event/notification to the associated
- * <tt>OtrContactMenu</tt> if it is still needed by the application.
- */
- public void sessionStatusChanged(Contact contact)
- {
- ScOtrEngineListener l = getListener();
-
- if (l != null)
- l.sessionStatusChanged(contact);
- }
- }
}
diff --git a/src/net/java/sip/communicator/plugin/otr/OtrMetaContactButton.java b/src/net/java/sip/communicator/plugin/otr/OtrMetaContactButton.java
index 2ca8bba..b5b5f12 100644
--- a/src/net/java/sip/communicator/plugin/otr/OtrMetaContactButton.java
+++ b/src/net/java/sip/communicator/plugin/otr/OtrMetaContactButton.java
@@ -8,17 +8,15 @@ package net.java.sip.communicator.plugin.otr;
import java.awt.*;
import java.awt.event.*;
-import java.io.*;
-
-import javax.imageio.*;
import net.java.otr4j.*;
import net.java.otr4j.session.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.gui.Container;
import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.plugin.desktoputil.*;
+import net.java.sip.communicator.util.*;
/**
* A {@link AbstractPluginComponent} that registers the Off-the-Record button in
@@ -28,69 +26,78 @@ import net.java.sip.communicator.plugin.desktoputil.*;
*/
public class OtrMetaContactButton
extends AbstractPluginComponent
+ implements ScOtrEngineListener,
+ ScOtrKeyManagerListener
{
+ /**
+ * The logger
+ */
+ private final Logger logger = Logger.getLogger(OtrMetaContactButton.class);
+
private SIPCommButton button;
private Contact contact;
- private final ScOtrEngineListener scOtrEngineListener =
- new ScOtrEngineListener()
+ /**
+ * The timer task that changes the padlock icon to "loading" and
+ * then to "broken" if the specified timeout passed
+ */
+ public void sessionStatusChanged(Contact contact)
+ {
+ // OtrMetaContactButton.this.contact can be null.
+ if (contact.equals(OtrMetaContactButton.this.contact))
{
- public void sessionStatusChanged(Contact contact)
- {
- // OtrMetaContactButton.this.contact can be null.
- if (contact.equals(OtrMetaContactButton.this.contact))
- {
- setStatus(
- OtrActivator.scOtrEngine.getSessionStatus(contact));
- }
- }
- public void contactPolicyChanged(Contact contact)
- {
- // OtrMetaContactButton.this.contact can be null.
- if (contact.equals(OtrMetaContactButton.this.contact))
- {
- setPolicy(
- OtrActivator.scOtrEngine.getContactPolicy(contact));
- }
- }
+ setStatus(
+ OtrActivator.scOtrEngine.getSessionStatus(contact));
+ }
+ }
- public void globalPolicyChanged()
- {
- if (OtrMetaContactButton.this.contact != null)
- setPolicy(
- OtrActivator.scOtrEngine.getContactPolicy(contact));
- }
- };
-
- private final ScOtrKeyManagerListener scOtrKeyManagerListener =
- new ScOtrKeyManagerListener()
+ public void contactPolicyChanged(Contact contact)
+ {
+ // OtrMetaContactButton.this.contact can be null.
+ if (contact.equals(OtrMetaContactButton.this.contact))
{
- public void contactVerificationStatusChanged(Contact contact)
- {
- // OtrMetaContactButton.this.contact can be null.
- if (contact.equals(OtrMetaContactButton.this.contact))
- {
- setStatus(
- OtrActivator.scOtrEngine.getSessionStatus(contact));
- }
- }
- };
+ setPolicy(
+ OtrActivator.scOtrEngine.getContactPolicy(contact));
+ }
+ }
+
+ public void globalPolicyChanged()
+ {
+ if (OtrMetaContactButton.this.contact != null)
+ setPolicy(
+ OtrActivator.scOtrEngine.getContactPolicy(contact));
+ }
+ public void contactVerificationStatusChanged(Contact contact)
+ {
+ // OtrMetaContactButton.this.contact can be null.
+ if (contact.equals(OtrMetaContactButton.this.contact))
+ {
+ setStatus(
+ OtrActivator.scOtrEngine.getSessionStatus(contact));
+ }
+ }
public OtrMetaContactButton(Container container,
PluginComponentFactory parentFactory)
{
super(container, parentFactory);
- OtrActivator.scOtrEngine.addListener(scOtrEngineListener);
- OtrActivator.scOtrKeyManager.addListener(scOtrKeyManagerListener);
- }
-
- void dispose()
- {
- OtrActivator.scOtrEngine.removeListener(scOtrEngineListener);
- OtrActivator.scOtrKeyManager.removeListener(scOtrKeyManagerListener);
+ /*
+ * XXX This OtrMetaContactButton instance cannot be added as a listener
+ * to scOtrEngine and scOtrKeyManager without being removed later on
+ * because the latter live forever. Unfortunately, the dispose() method
+ * of this instance is never executed. OtrWeakListener will keep this
+ * instance as a listener of scOtrEngine and scOtrKeyManager for as long
+ * as this instance is necessary. And this instance will be strongly
+ * referenced by the JMenuItems which depict it. So when the JMenuItems
+ * are gone, this instance will become obsolete and OtrWeakListener will
+ * remove it as a listener of scOtrEngine and scOtrKeyManager.
+ */
+ new OtrWeakListener<OtrMetaContactButton>(
+ this,
+ OtrActivator.scOtrEngine, OtrActivator.scOtrKeyManager);
}
/**
@@ -122,13 +129,15 @@ public class OtrMetaContactButton
{
case ENCRYPTED:
case FINISHED:
- // Default action for finished and encrypted sessions is
- // end session.
+ case LOADING:
+ // Default action for finished, encrypted and loading
+ // sessions is end session.
OtrActivator.scOtrEngine.endSession(contact);
break;
+ case TIMED_OUT:
case PLAINTEXT:
- // Default action for finished and plaintext sessions is
- // start session.
+ // Default action for timed_out and plaintext sessions
+ // is start session.
OtrActivator.scOtrEngine.startSession(contact);
break;
}
@@ -173,7 +182,7 @@ public class OtrMetaContactButton
}
else
{
- this.setStatus(SessionStatus.PLAINTEXT);
+ this.setStatus(ScSessionStatus.PLAINTEXT);
this.setPolicy(null);
}
}
@@ -205,7 +214,7 @@ public class OtrMetaContactButton
*
* @param status the {@link SessionStatus}.
*/
- private void setStatus(SessionStatus status)
+ private void setStatus(ScSessionStatus status)
{
String urlKey;
String tipKey;
@@ -216,31 +225,37 @@ public class OtrMetaContactButton
= OtrActivator.scOtrKeyManager.isVerified(contact)
? "plugin.otr.ENCRYPTED_ICON_22x22"
: "plugin.otr.ENCRYPTED_UNVERIFIED_ICON_22x22";
- tipKey = "plugin.otr.menu.END_OTR";
+ tipKey =
+ OtrActivator.scOtrKeyManager.isVerified(contact)
+ ? "plugin.otr.menu.VERIFIED"
+ : "plugin.otr.menu.UNVERIFIED";
break;
case FINISHED:
urlKey = "plugin.otr.FINISHED_ICON_22x22";
- tipKey = "plugin.otr.menu.END_OTR";
+ tipKey = "plugin.otr.menu.FINISHED";
break;
case PLAINTEXT:
urlKey = "plugin.otr.PLAINTEXT_ICON_22x22";
tipKey = "plugin.otr.menu.START_OTR";
break;
+ case LOADING:
+ urlKey = "plugin.otr.LOADING_ICON_22x22";
+ tipKey = "plugin.otr.menu.LOADING_OTR";
+ break;
+ case TIMED_OUT:
+ urlKey = "plugin.otr.BROKEN_ICON_22x22";
+ tipKey = "plugin.otr.menu.TIMED_OUT";
+ break;
default:
return;
}
- try
- {
- SIPCommButton button = getButton();
- button.setImage(
- ImageIO.read(OtrActivator.resourceService.getImageURL(urlKey)));
- button.setToolTipText(OtrActivator.resourceService
- .getI18NString(tipKey));
- }
- catch (IOException e)
- {
- e.printStackTrace();
- }
+ SIPCommButton button = getButton();
+ button.setIconImage(
+ Toolkit.getDefaultToolkit().getImage(
+ OtrActivator.resourceService.getImageURL(urlKey)));
+ button.setToolTipText(OtrActivator.resourceService
+ .getI18NString(tipKey));
+ button.repaint();
}
}
diff --git a/src/net/java/sip/communicator/plugin/otr/OtrTransformLayer.java b/src/net/java/sip/communicator/plugin/otr/OtrTransformLayer.java
index bedbe32..5111d9e 100644
--- a/src/net/java/sip/communicator/plugin/otr/OtrTransformLayer.java
+++ b/src/net/java/sip/communicator/plugin/otr/OtrTransformLayer.java
@@ -7,7 +7,6 @@
package net.java.sip.communicator.plugin.otr;
import net.java.otr4j.*;
-import net.java.otr4j.session.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
@@ -27,12 +26,13 @@ public class OtrTransformLayer
Contact contact = evt.getDestinationContact();
OtrPolicy policy = OtrActivator.scOtrEngine.getContactPolicy(contact);
- SessionStatus sessionStatus =
+ ScSessionStatus sessionStatus =
OtrActivator.scOtrEngine.getSessionStatus(contact);
// If OTR is disabled and we are not over an encrypted session, don't
// process anything.
if (!policy.getEnableManual()
- && sessionStatus == SessionStatus.PLAINTEXT)
+ && sessionStatus != ScSessionStatus.ENCRYPTED
+ && sessionStatus != ScSessionStatus.FINISHED)
return evt;
if (OtrActivator.scOtrEngine.isMessageUIDInjected(evt
@@ -63,12 +63,13 @@ public class OtrTransformLayer
Contact contact = evt.getDestinationContact();
OtrPolicy policy = OtrActivator.scOtrEngine.getContactPolicy(contact);
- SessionStatus sessionStatus =
+ ScSessionStatus sessionStatus =
OtrActivator.scOtrEngine.getSessionStatus(contact);
// If OTR is disabled and we are not over an encrypted session, don't
// process anything.
if (!policy.getEnableManual()
- && sessionStatus == SessionStatus.PLAINTEXT)
+ && sessionStatus != ScSessionStatus.ENCRYPTED
+ && sessionStatus != ScSessionStatus.FINISHED)
return evt;
// If this is a message otr4j injected earlier, return the event as is.
@@ -111,12 +112,13 @@ public class OtrTransformLayer
Contact contact = evt.getSourceContact();
OtrPolicy policy = OtrActivator.scOtrEngine.getContactPolicy(contact);
- SessionStatus sessionStatus =
+ ScSessionStatus sessionStatus =
OtrActivator.scOtrEngine.getSessionStatus(contact);
// If OTR is disabled and we are not over an encrypted session, don't
// process anything.
if (!policy.getEnableManual()
- && sessionStatus == SessionStatus.PLAINTEXT)
+ && sessionStatus != ScSessionStatus.ENCRYPTED
+ && sessionStatus != ScSessionStatus.FINISHED)
return evt;
// Process the incoming message.
diff --git a/src/net/java/sip/communicator/plugin/otr/OtrWeakListener.java b/src/net/java/sip/communicator/plugin/otr/OtrWeakListener.java
new file mode 100644
index 0000000..423cf23
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/otr/OtrWeakListener.java
@@ -0,0 +1,155 @@
+/*
+ * 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.otr;
+
+import java.lang.ref.*;
+
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * Implements a <tt>ScOtrEngineListener</tt> and
+ * <tt>ScOtrKeyManagerListener</tt> listener for the purposes of
+ * <tt>OtrContactMenu</tt> and <tt>OtrMetaContactButton</tt> which listen to
+ * <tt>ScOtrEngine</tt> and <tt>ScOtrKeyManager</tt> while weakly referencing
+ * them. Fixes a memory leak of <tt>OtrContactMenu</tt> and
+ * <tt>OtrMetaContactButton</tt> instances because these cannot determine when
+ * they are to be explicitly disposed.
+ *
+ * @author Lyubomir Marinov
+ */
+public class OtrWeakListener
+ <T extends ScOtrEngineListener &
+ ScOtrKeyManagerListener>
+ implements ScOtrEngineListener,
+ ScOtrKeyManagerListener
+{
+ /**
+ * The <tt>ScOtrEngine</tt> the <tt>T</tt> associated with
+ * this instance is to listen to.
+ */
+ private final ScOtrEngine engine;
+
+ /**
+ * The <tt>ScOtrKeyManager</tt> the <tt>T</tt> associated
+ * with this instance is to listen to.
+ */
+ private final ScOtrKeyManager keyManager;
+
+ /**
+ * The <tt>T</tt> which is associated with this instance
+ * and which is to listen to {@link #engine} and {@link #keyManager}.
+ */
+ private final WeakReference<T> listener;
+
+ /**
+ * Initializes a new <tt>OtrWeakListener</tt> instance which is to allow
+ * a specific <tt>T</tt> to listener to a specific
+ * <tt>ScOtrEngine</tt> and a specific <tt>ScOtrKeyManager</tt> without
+ * being retained by them forever (because they live forever).
+ *
+ * @param listener the <tt>T</tt> which is to listen to the
+ * specified <tt>engine</tt> and <tt>keyManager</tt>
+ * @param engine the <tt>ScOtrEngine</tt> which is to be listened to by
+ * the specified <tt>T</tt>
+ * @param keyManager the <tt>ScOtrKeyManager</tt> which is to be
+ * listened to by the specified <tt>T</tt>
+ */
+ public OtrWeakListener(
+ T listener,
+ ScOtrEngine engine, ScOtrKeyManager keyManager)
+ {
+ if (listener == null)
+ throw new NullPointerException("listener");
+
+ this.listener = new WeakReference<T>(listener);
+ this.engine = engine;
+ this.keyManager = keyManager;
+
+ this.engine.addListener(this);
+ this.keyManager.addListener(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Forwards the event/notification to the associated
+ * <tt>T</tt> if it is still needed by the application.
+ */
+ public void contactPolicyChanged(Contact contact)
+ {
+ ScOtrEngineListener l = getListener();
+
+ if (l != null)
+ l.contactPolicyChanged(contact);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Forwards the event/notification to the associated
+ * <tt>T</tt> if it is still needed by the application.
+ */
+ public void contactVerificationStatusChanged(Contact contact)
+ {
+ ScOtrKeyManagerListener l = getListener();
+
+ if (l != null)
+ l.contactVerificationStatusChanged(contact);
+ }
+
+ /**
+ * Gets the <tt>T</tt> which is listening to {@link #engine}
+ * and {@link #keyManager}. If the <tt>T</tt> is no longer needed by
+ * the application, this instance seizes listening to <tt>engine</tt> and
+ * <tt>keyManager</tt> and allows the memory used by this instance to be
+ * reclaimed by the Java virtual machine.
+ *
+ * @return the <tt>T</tt> which is listening to
+ * <tt>engine</tt> and <tt>keyManager</tt> if it is still needed by the
+ * application; otherwise, <tt>null</tt>
+ */
+ private T getListener()
+ {
+ T l = this.listener.get();
+
+ if (l == null)
+ {
+ engine.removeListener(this);
+ keyManager.removeListener(this);
+ }
+
+ return l;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Forwards the event/notification to the associated
+ * <tt>T</tt> if it is still needed by the application.
+ */
+ public void globalPolicyChanged()
+ {
+ ScOtrEngineListener l = getListener();
+
+ if (l != null)
+ l.globalPolicyChanged();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Forwards the event/notification to the associated
+ * <tt>T</tt> if it is still needed by the application.
+ */
+ public void sessionStatusChanged(Contact contact)
+ {
+ ScOtrEngineListener l = getListener();
+
+ if (l != null)
+ l.sessionStatusChanged(contact);
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/otr/ScOtrEngine.java b/src/net/java/sip/communicator/plugin/otr/ScOtrEngine.java
index dfa0803..853c5fc 100644
--- a/src/net/java/sip/communicator/plugin/otr/ScOtrEngine.java
+++ b/src/net/java/sip/communicator/plugin/otr/ScOtrEngine.java
@@ -7,7 +7,6 @@
package net.java.sip.communicator.plugin.otr;
import net.java.otr4j.*;
-import net.java.otr4j.session.*;
import net.java.sip.communicator.service.protocol.*;
/**
@@ -101,13 +100,13 @@ public interface ScOtrEngine
public abstract void refreshSession(Contact contact);
/**
- * Gets the {@link SessionStatus} for the given {@link Contact}.
+ * Gets the {@link ScSessionStatus} for the given {@link Contact}.
*
- * @param contact the {@link Contact} whose {@link SessionStatus} we are
+ * @param contact the {@link Contact} whose {@link ScSessionStatus} we are
* interested in.
- * @return the {@link SessionStatus}.
+ * @return the {@link ScSessionStatus}.
*/
- public abstract SessionStatus getSessionStatus(Contact contact);
+ public abstract ScSessionStatus getSessionStatus(Contact contact);
// New Methods (Misc)
diff --git a/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java b/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java
index 594c8f1..b1797fd 100644
--- a/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java
+++ b/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java
@@ -314,6 +314,27 @@ public class ScOtrEngineImpl
}
}
+ /**
+ * The max timeout period elapsed prior to establishing a TIMED_OUT session.
+ */
+ private static final int SESSION_TIMEOUT =
+ OtrActivator.configService.getInt(
+ "net.java.sip.communicator.plugin.otr.SESSION_STATUS_TIMEOUT",
+ 30000);
+
+ /**
+ * Manages the scheduling of TimerTasks that are used to set Contact's
+ * ScSessionStatus (to TIMED_OUT) after a period of time.
+ */
+ private ScSessionStatusScheduler scheduler = new ScSessionStatusScheduler();
+
+ /**
+ * This mapping is used for taking care of keeping SessionStatus and
+ * ScSessionStatus in sync for every Session object.
+ */
+ private Map<SessionID, ScSessionStatus> scSessionStatusMap =
+ new ConcurrentHashMap<SessionID, ScSessionStatus>();
+
private static final Map<ScSessionID, Contact> contactsMap =
new Hashtable<ScSessionID, Contact>();
@@ -388,6 +409,7 @@ public class ScOtrEngineImpl
// Clears the map after previous instance
// This is required because of OSGi restarts in the same VM on Android
contactsMap.clear();
+ scSessionStatusMap.clear();
this.otrEngine.addOtrEngineListener(new OtrEngineListener()
{
@@ -397,10 +419,17 @@ public class ScOtrEngineImpl
if (contact == null)
return;
+ // Cancels any scheduled tasks that will change the
+ // ScSessionStatus for this Contact
+ scheduler.cancel(contact);
+
+ ScSessionStatus scSessionStatus = getSessionStatus(contact);
String message = "";
switch (otrEngine.getSessionStatus(sessionID))
{
case ENCRYPTED:
+ scSessionStatus = ScSessionStatus.ENCRYPTED;
+ scSessionStatusMap.put(sessionID, scSessionStatus);
PublicKey remotePubKey =
otrEngine.getRemotePublicKey(sessionID);
@@ -490,6 +519,8 @@ public class ScOtrEngineImpl
break;
case FINISHED:
+ scSessionStatus = ScSessionStatus.FINISHED;
+ scSessionStatusMap.put(sessionID, scSessionStatus);
message =
OtrActivator.resourceService.getI18NString(
"plugin.otr.activator.sessionfinished",
@@ -497,6 +528,8 @@ public class ScOtrEngineImpl
{ contact.getDisplayName() });
break;
case PLAINTEXT:
+ scSessionStatus = ScSessionStatus.PLAINTEXT;
+ scSessionStatusMap.put(sessionID, scSessionStatus);
message =
OtrActivator.resourceService.getI18NString(
"plugin.otr.activator.sessionlost", new String[]
@@ -575,6 +608,8 @@ public class ScOtrEngineImpl
SessionID sessionID = getSessionID(contact);
try
{
+ setSessionStatus(contact, ScSessionStatus.PLAINTEXT);
+
otrEngine.endSession(sessionID);
}
catch (OtrException e)
@@ -617,9 +652,73 @@ public class ScOtrEngineImpl
}
}
- public SessionStatus getSessionStatus(Contact contact)
+ /**
+ * Manages the scheduling of TimerTasks that are used to set Contact's
+ * ScSessionStatus after a period of time.
+ *
+ * @author Marin Dzhigarov
+ */
+ private class ScSessionStatusScheduler
+ {
+ private final Timer timer = new Timer();
+
+ private final Map<Contact, TimerTask> tasks =
+ new ConcurrentHashMap<Contact, TimerTask>();
+
+ public void scheduleScSessionStatusChange(
+ final Contact contact, final ScSessionStatus status)
+ {
+ cancel(contact);
+
+ TimerTask task = new TimerTask() {
+ @Override
+ public void run()
+ {
+ setSessionStatus(contact, status);
+ }
+ };
+ timer.schedule(task, SESSION_TIMEOUT);
+ tasks.put(contact, task);
+ }
+
+ public void cancel(final Contact contact)
+ {
+ TimerTask task = tasks.get(contact);
+ if (task != null)
+ task.cancel();
+ tasks.remove(contact);
+ }
+ }
+
+ private void setSessionStatus(Contact contact, ScSessionStatus status)
+ {
+ scSessionStatusMap.put(getSessionID(contact), status);
+ for (ScOtrEngineListener l : getListeners())
+ l.sessionStatusChanged(contact);
+ }
+
+ public ScSessionStatus getSessionStatus(Contact contact)
{
- return otrEngine.getSessionStatus(getSessionID(contact));
+ SessionID sessionID = getSessionID(contact);
+ SessionStatus sessionStatus = otrEngine.getSessionStatus(sessionID);
+ ScSessionStatus scSessionStatus = null;
+ if (!scSessionStatusMap.containsKey(sessionID))
+ {
+ switch (sessionStatus)
+ {
+ case PLAINTEXT:
+ scSessionStatus = ScSessionStatus.PLAINTEXT;
+ break;
+ case ENCRYPTED:
+ scSessionStatus = ScSessionStatus.ENCRYPTED;
+ break;
+ case FINISHED:
+ scSessionStatus = ScSessionStatus.FINISHED;
+ break;
+ }
+ scSessionStatusMap.put(sessionID, scSessionStatus);
+ }
+ return scSessionStatusMap.get(sessionID);
}
public boolean isMessageUIDInjected(String mUID)
@@ -751,6 +850,17 @@ public class ScOtrEngineImpl
public void startSession(Contact contact)
{
SessionID sessionID = getSessionID(contact);
+
+ ScSessionStatus scSessionStatus = getSessionStatus(contact);
+ scSessionStatus = ScSessionStatus.LOADING;
+ scSessionStatusMap.put(sessionID, scSessionStatus);
+ for (ScOtrEngineListener l : getListeners())
+ {
+ l.sessionStatusChanged(contact);
+ scheduler.scheduleScSessionStatusChange(
+ contact, ScSessionStatus.TIMED_OUT);
+ }
+
try
{
otrEngine.startSession(sessionID);
diff --git a/src/net/java/sip/communicator/plugin/otr/ScSessionStatus.java b/src/net/java/sip/communicator/plugin/otr/ScSessionStatus.java
new file mode 100644
index 0000000..6151db5
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/otr/ScSessionStatus.java
@@ -0,0 +1,29 @@
+/*
+ * 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.otr;
+
+/**
+ * Extends otr4j's <tt>SessionStatus</tt> with two additional states.
+ *
+ * @author Marin Dzhigarov
+ */
+public enum ScSessionStatus
+{
+ PLAINTEXT,
+ ENCRYPTED,
+ FINISHED,
+ /*
+ * A Session transitions in LOADING state right before
+ * Session.startSession() is invoked.
+ */
+ LOADING,
+ /*
+ * A Session transitions in TIMED_OUT state after being in LOADING state for
+ * a long period of time.
+ */
+ TIMED_OUT
+}