mirror of https://github.com/sipwise/jitsi.git
Commits the patch of Sebastien Vincent provided on the dev mailing list in an e-mail with the subject "[Patch] Warnings" which fixes multiple warnings about unnecessary casts, missing or incorrect javadocs.
parent
d10540987f
commit
dfb0c91c94
@ -1,117 +1,117 @@
|
||||
/*
|
||||
* 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.impl.gui.main.call;
|
||||
|
||||
import java.awt.event.*;
|
||||
|
||||
import net.java.sip.communicator.impl.gui.*;
|
||||
import net.java.sip.communicator.impl.gui.utils.*;
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
import net.java.sip.communicator.util.swing.*;
|
||||
|
||||
/**
|
||||
* @author Lubomir Marinov
|
||||
*/
|
||||
public class LocalVideoButton
|
||||
extends SIPCommToggleButton
|
||||
{
|
||||
private static final Logger logger
|
||||
= Logger.getLogger(LocalVideoButton.class);
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* Creates a <tt>LocalVideoButton</tt> by specifying the corresponding
|
||||
* <tt>call</tt>.
|
||||
* @param call the corresponding to this button call
|
||||
*/
|
||||
public LocalVideoButton(Call call)
|
||||
{
|
||||
setBgImage(ImageLoader.getImage(ImageLoader.CALL_SETTING_BUTTON_BG));
|
||||
setBgRolloverImage(
|
||||
ImageLoader.getImage(ImageLoader.CALL_SETTING_BUTTON_BG));
|
||||
setIconImage(ImageLoader.getImage(ImageLoader.LOCAL_VIDEO_BUTTON));
|
||||
setPressedImage(
|
||||
ImageLoader.getImage(ImageLoader.CALL_SETTING_BUTTON_PRESSED_BG));
|
||||
|
||||
setModel(new LocalVideoButtonModel(call));
|
||||
setToolTipText(GuiActivator.getResources().getI18NString(
|
||||
"service.gui.LOCAL_VIDEO_BUTTON_TOOL_TIP"));
|
||||
}
|
||||
|
||||
private static class LocalVideoButtonModel
|
||||
extends ToggleButtonModel
|
||||
implements ActionListener,
|
||||
Runnable
|
||||
{
|
||||
private final Call call;
|
||||
|
||||
private Thread runner;
|
||||
|
||||
public LocalVideoButtonModel(Call call)
|
||||
{
|
||||
this.call = call;
|
||||
|
||||
addActionListener(this);
|
||||
}
|
||||
|
||||
public synchronized void actionPerformed(ActionEvent event)
|
||||
{
|
||||
if (runner == null)
|
||||
{
|
||||
runner = new Thread(this, LocalVideoButton.class.getName());
|
||||
runner.setDaemon(true);
|
||||
|
||||
setEnabled(false);
|
||||
runner.start();
|
||||
}
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
doRun();
|
||||
}
|
||||
finally
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
if (Thread.currentThread().equals(runner))
|
||||
{
|
||||
runner = null;
|
||||
setEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doRun()
|
||||
{
|
||||
OperationSetVideoTelephony telephony = (OperationSetVideoTelephony)
|
||||
call.getProtocolProvider()
|
||||
.getOperationSet(OperationSetVideoTelephony.class);
|
||||
|
||||
if (telephony != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
telephony.setLocalVideoAllowed(
|
||||
call,
|
||||
!telephony.isLocalVideoAllowed(call));
|
||||
}
|
||||
catch (OperationFailedException ex)
|
||||
{
|
||||
logger.error(
|
||||
"Failed to toggle the streaming of local video.",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.impl.gui.main.call;
|
||||
|
||||
import java.awt.event.*;
|
||||
|
||||
import net.java.sip.communicator.impl.gui.*;
|
||||
import net.java.sip.communicator.impl.gui.utils.*;
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
import net.java.sip.communicator.util.swing.*;
|
||||
|
||||
/**
|
||||
* @author Lubomir Marinov
|
||||
*/
|
||||
public class LocalVideoButton
|
||||
extends SIPCommToggleButton
|
||||
{
|
||||
private static final Logger logger
|
||||
= Logger.getLogger(LocalVideoButton.class);
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* Creates a <tt>LocalVideoButton</tt> by specifying the corresponding
|
||||
* <tt>call</tt>.
|
||||
* @param call the corresponding to this button call
|
||||
*/
|
||||
public LocalVideoButton(Call call)
|
||||
{
|
||||
setBgImage(ImageLoader.getImage(ImageLoader.CALL_SETTING_BUTTON_BG));
|
||||
setBgRolloverImage(
|
||||
ImageLoader.getImage(ImageLoader.CALL_SETTING_BUTTON_BG));
|
||||
setIconImage(ImageLoader.getImage(ImageLoader.LOCAL_VIDEO_BUTTON));
|
||||
setPressedImage(
|
||||
ImageLoader.getImage(ImageLoader.CALL_SETTING_BUTTON_PRESSED_BG));
|
||||
|
||||
setModel(new LocalVideoButtonModel(call));
|
||||
setToolTipText(GuiActivator.getResources().getI18NString(
|
||||
"service.gui.LOCAL_VIDEO_BUTTON_TOOL_TIP"));
|
||||
}
|
||||
|
||||
private static class LocalVideoButtonModel
|
||||
extends ToggleButtonModel
|
||||
implements ActionListener,
|
||||
Runnable
|
||||
{
|
||||
private final Call call;
|
||||
|
||||
private Thread runner;
|
||||
|
||||
public LocalVideoButtonModel(Call call)
|
||||
{
|
||||
this.call = call;
|
||||
|
||||
addActionListener(this);
|
||||
}
|
||||
|
||||
public synchronized void actionPerformed(ActionEvent event)
|
||||
{
|
||||
if (runner == null)
|
||||
{
|
||||
runner = new Thread(this, LocalVideoButton.class.getName());
|
||||
runner.setDaemon(true);
|
||||
|
||||
setEnabled(false);
|
||||
runner.start();
|
||||
}
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
doRun();
|
||||
}
|
||||
finally
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
if (Thread.currentThread().equals(runner))
|
||||
{
|
||||
runner = null;
|
||||
setEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doRun()
|
||||
{
|
||||
OperationSetVideoTelephony telephony =
|
||||
call.getProtocolProvider()
|
||||
.getOperationSet(OperationSetVideoTelephony.class);
|
||||
|
||||
if (telephony != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
telephony.setLocalVideoAllowed(
|
||||
call,
|
||||
!telephony.isLocalVideoAllowed(call));
|
||||
}
|
||||
catch (OperationFailedException ex)
|
||||
{
|
||||
logger.error(
|
||||
"Failed to toggle the streaming of local video.",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,416 +1,416 @@
|
||||
/*
|
||||
* 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.impl.protocol;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.osgi.framework.*;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.service.protocol.event.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
/**
|
||||
* Imposes the policy to have one call in progress i.e. to put existing calls on
|
||||
* hold when a new call enters in progress.
|
||||
*
|
||||
* @author Lubomir Marinov
|
||||
*/
|
||||
public class SingleCallInProgressPolicy
|
||||
{
|
||||
|
||||
/**
|
||||
* The name of the configuration property which specifies whether
|
||||
* <code>SingleCallInProgressPolicy</code> is enabled i.e. whether it should
|
||||
* put existing calls on hold when a new call enters in progress.
|
||||
*/
|
||||
private static final String PNAME_SINGLE_CALL_IN_PROGRESS_POLICY_ENABLED
|
||||
= "net.java.sip.communicator.impl.protocol.SingleCallInProgressPolicy.enabled";
|
||||
|
||||
/**
|
||||
* Implements the listeners interfaces used by this policy.
|
||||
*/
|
||||
private class SingleCallInProgressPolicyListener
|
||||
implements CallChangeListener,
|
||||
CallListener,
|
||||
ServiceListener
|
||||
{
|
||||
/**
|
||||
* Stops tracking the state of a specific <code>Call</code> and no
|
||||
* longer tries to put it on hold when it ends.
|
||||
*
|
||||
* @see CallListener#callEnded(CallEvent)
|
||||
*/
|
||||
public void callEnded(CallEvent callEvent)
|
||||
{
|
||||
SingleCallInProgressPolicy.this.handleCallEvent(
|
||||
CallEvent.CALL_ENDED, callEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing because adding <code>CallPeer<code>s to
|
||||
* <code>Call</code>s isn't related to the policy to put existing calls
|
||||
* on hold when a new call becomes in-progress and just implements
|
||||
* <code>CallChangeListener</code>.
|
||||
*
|
||||
* @see CallChangeListener#callPeerAdded(CallPeerEvent)
|
||||
*/
|
||||
public void callPeerAdded( CallPeerEvent callPeerEvent)
|
||||
{
|
||||
|
||||
/*
|
||||
* Not of interest, just implementing CallChangeListener in which
|
||||
* only #callStateChanged(CallChangeEvent) is of interest.
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing because removing <code>CallPeer<code>s to
|
||||
* <code>Call</code>s isn't related to the policy to put existing calls
|
||||
* on hold when a new call becomes in-progress and just implements
|
||||
* <code>CallChangeListener</code>.
|
||||
*
|
||||
* @see CallChangeListener#callPeerRemoved(CallPeerEvent)
|
||||
*/
|
||||
public void callPeerRemoved( CallPeerEvent callPeerEvent)
|
||||
{
|
||||
|
||||
/*
|
||||
* Not of interest, just implementing CallChangeListener in which
|
||||
* only #callStateChanged(CallChangeEvent) is of interest.
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Upon a <code>Call</code> changing its state to
|
||||
* <code>CallState.CALL_IN_PROGRESS</code>, puts the other existing
|
||||
* <code>Call</code>s on hold.
|
||||
*
|
||||
* @param callChangeEvent the <tt>CallChangeEvent</tt> that we are to
|
||||
* deliver.
|
||||
*
|
||||
* @see CallChangeListener#callStateChanged(CallChangeEvent)
|
||||
*/
|
||||
public void callStateChanged(CallChangeEvent callChangeEvent)
|
||||
{
|
||||
SingleCallInProgressPolicy.this.callStateChanged(callChangeEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remembers an incoming <code>Call</code> so that it can put the other
|
||||
* existing <code>Call</code>s on hold when it changes its state to
|
||||
* <code>CallState.CALL_IN_PROGRESS</code>.
|
||||
*
|
||||
* @see CallListener#incomingCallReceived(CallEvent)
|
||||
*/
|
||||
public void incomingCallReceived(CallEvent callEvent)
|
||||
{
|
||||
SingleCallInProgressPolicy.this.handleCallEvent(
|
||||
CallEvent.CALL_RECEIVED, callEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remembers an outgoing <code>Call</code> so that it can put the other
|
||||
* existing <code>Call</code>s on hold when it changes its state to
|
||||
* <code>CallState.CALL_IN_PROGRESS</code>.
|
||||
*
|
||||
* @see CallListener#outgoingCallCreated(CallEvent)
|
||||
*/
|
||||
public void outgoingCallCreated(CallEvent callEvent)
|
||||
{
|
||||
SingleCallInProgressPolicy.this.handleCallEvent(
|
||||
CallEvent.CALL_INITIATED, callEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts/stops tracking the new <code>Call</code>s originating from a
|
||||
* specific <code>ProtocolProviderService</code> when it
|
||||
* registers/unregisters in order to take them into account when putting
|
||||
* existing calls on hold upon a new call entering its in-progress
|
||||
* state.
|
||||
*
|
||||
* @param serviceEvent
|
||||
* the <code>ServiceEvent</code> event describing a change in
|
||||
* the state of a service registration which may be a
|
||||
* <code>ProtocolProviderService</code> supporting
|
||||
* <code>OperationSetBasicTelephony</code> and thus being
|
||||
* able to create new <code>Call</code>s
|
||||
*/
|
||||
public void serviceChanged(ServiceEvent serviceEvent)
|
||||
{
|
||||
SingleCallInProgressPolicy.this.serviceChanged(serviceEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Our class logger
|
||||
*/
|
||||
private static final Logger logger =
|
||||
Logger.getLogger(SingleCallInProgressPolicy.class);
|
||||
|
||||
/**
|
||||
* The <code>BundleContext</code> to the Calls of which this policy applies.
|
||||
*/
|
||||
private final BundleContext bundleContext;
|
||||
|
||||
/**
|
||||
* The <code>Call</code>s this policy manages i.e. put on hold when one of
|
||||
* them enters in progress.
|
||||
*/
|
||||
private final List<Call> calls = new ArrayList<Call>();
|
||||
|
||||
/**
|
||||
* The listener utilized by this policy to discover new <code>Call</code>
|
||||
* and track their in-progress state.
|
||||
*/
|
||||
private final SingleCallInProgressPolicyListener listener =
|
||||
new SingleCallInProgressPolicyListener();
|
||||
|
||||
/**
|
||||
* Initializes a new <code>SingleCallInProgressPolicy</code> instance which
|
||||
* will apply to the <code>Call</code>s of a specific
|
||||
* <code>BundleContext</code>.
|
||||
*
|
||||
* @param bundleContext
|
||||
* the <code>BundleContext</code> to the
|
||||
* <code>Call<code>s of which the new policy should apply
|
||||
*/
|
||||
public SingleCallInProgressPolicy(BundleContext bundleContext)
|
||||
{
|
||||
this.bundleContext = bundleContext;
|
||||
|
||||
this.bundleContext.addServiceListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a specific <code>Call</code> with this policy in order to have
|
||||
* the rules of the latter apply to the former.
|
||||
*
|
||||
* @param call
|
||||
* the <code>Call</code> to register with this policy in order to
|
||||
* have the rules of the latter apply to the former
|
||||
*/
|
||||
private void addCallListener(Call call)
|
||||
{
|
||||
synchronized (calls)
|
||||
{
|
||||
if (!calls.contains(call))
|
||||
{
|
||||
CallState callState = call.getCallState();
|
||||
|
||||
if ((callState != null)
|
||||
&& !callState.equals(CallState.CALL_ENDED))
|
||||
{
|
||||
calls.add(call);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
call.addCallChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a specific <code>OperationSetBasicTelephony</code> with this
|
||||
* policy in order to have the rules of the latter apply to the
|
||||
* <code>Call</code>s created by the former.
|
||||
*
|
||||
* @param telephony
|
||||
* the <code>OperationSetBasicTelephony</code> to register with
|
||||
* this policy in order to have the rules of the latter apply to
|
||||
* the <code>Call</code>s created by the former
|
||||
*/
|
||||
private void addOperationSetBasicTelephonyListener(
|
||||
OperationSetBasicTelephony telephony)
|
||||
{
|
||||
telephony.addCallListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles changes in the state of a <code>Call</code> this policy applies
|
||||
* to in order to detect when new calls become in-progress and when the
|
||||
* other calls should be put on hold.
|
||||
*
|
||||
* @param callChangeEvent
|
||||
* a <code>CallChangeEvent</code> value which describes the
|
||||
* <code>Call</code> and the change in its state
|
||||
*/
|
||||
private void callStateChanged(CallChangeEvent callChangeEvent)
|
||||
{
|
||||
Call call = callChangeEvent.getSourceCall();
|
||||
|
||||
if (CallState.CALL_INITIALIZATION.equals(callChangeEvent.getOldValue())
|
||||
&& CallState.CALL_IN_PROGRESS.equals(call.getCallState())
|
||||
&& ProtocolProviderActivator
|
||||
.getConfigurationService()
|
||||
.getBoolean(
|
||||
PNAME_SINGLE_CALL_IN_PROGRESS_POLICY_ENABLED,
|
||||
true))
|
||||
{
|
||||
synchronized (calls)
|
||||
{
|
||||
for (Call otherCall : calls)
|
||||
if (!call.equals(otherCall)
|
||||
&& CallState.CALL_IN_PROGRESS
|
||||
.equals(otherCall.getCallState()))
|
||||
putOnHold(otherCall);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs end-of-life cleanup associated with this instance e.g. removes
|
||||
* added listeners.
|
||||
*/
|
||||
public void dispose()
|
||||
{
|
||||
bundleContext.removeServiceListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the start and end of the <code>Call</code>s this policy applies
|
||||
* to in order to have them or stop having them put the other existing calls
|
||||
* on hold when the former change their states to
|
||||
* <code>CallState.CALL_IN_PROGRESS</code>.
|
||||
*
|
||||
* @param type
|
||||
* one of {@link CallEvent#CALL_ENDED},
|
||||
* {@link CallEvent#CALL_INITIATED} and
|
||||
* {@link CallEvent#CALL_RECEIVED} which described the type of
|
||||
* the event to be handled
|
||||
* @param callEvent
|
||||
* a <code>CallEvent</code> value which describes the change and
|
||||
* the <code>Call</code> associated with it
|
||||
*/
|
||||
private void handleCallEvent(int type, CallEvent callEvent)
|
||||
{
|
||||
Call call = callEvent.getSourceCall();
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case CallEvent.CALL_ENDED:
|
||||
removeCallListener(call);
|
||||
break;
|
||||
|
||||
case CallEvent.CALL_INITIATED:
|
||||
case CallEvent.CALL_RECEIVED:
|
||||
addCallListener(call);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the <code>CallPeer</code>s of a specific <code>Call</code> on
|
||||
* hold.
|
||||
*
|
||||
* @param call
|
||||
* the <code>Call</code> the <code>CallPeer</code>s of
|
||||
* which are to be put on hold
|
||||
*/
|
||||
private void putOnHold(Call call)
|
||||
{
|
||||
OperationSetBasicTelephony telephony =
|
||||
(OperationSetBasicTelephony) call.getProtocolProvider()
|
||||
.getOperationSet(OperationSetBasicTelephony.class);
|
||||
|
||||
if (telephony != null)
|
||||
{
|
||||
for (Iterator<? extends CallPeer> peerIter =
|
||||
call.getCallPeers(); peerIter.hasNext();)
|
||||
{
|
||||
CallPeer peer = peerIter.next();
|
||||
CallPeerState peerState = peer.getState();
|
||||
|
||||
if (!CallPeerState.DISCONNECTED.equals(peerState)
|
||||
&& !CallPeerState.FAILED.equals(peerState)
|
||||
&& !CallPeerState.isOnHold(peerState))
|
||||
{
|
||||
try
|
||||
{
|
||||
telephony.putOnHold(peer);
|
||||
}
|
||||
catch (OperationFailedException ex)
|
||||
{
|
||||
logger.error("Failed to put " + peer
|
||||
+ " on hold.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a specific <code>Call</code> from this policy in order to
|
||||
* have the rules of the latter no longer applied to the former.
|
||||
*
|
||||
* @param call
|
||||
* the <code>Call</code> to unregister from this policy in order
|
||||
* to have the rules of the latter no longer apply to the former
|
||||
*/
|
||||
private void removeCallListener(Call call)
|
||||
{
|
||||
call.removeCallChangeListener(listener);
|
||||
|
||||
synchronized (calls)
|
||||
{
|
||||
calls.remove(call);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a specific <code>OperationSetBasicTelephony</code> from this
|
||||
* policy in order to have the rules of the latter no longer apply to the
|
||||
* <code>Call</code>s created by the former.
|
||||
*
|
||||
* @param telephony
|
||||
* the <code>OperationSetBasicTelephony</code> to unregister from
|
||||
* this policy in order to have the rules of the latter apply to
|
||||
* the <code>Call</code>s created by the former
|
||||
*/
|
||||
private void removeOperationSetBasicTelephonyListener(
|
||||
OperationSetBasicTelephony telephony)
|
||||
{
|
||||
telephony.removeCallListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the registering and unregistering of
|
||||
* <code>OperationSetBasicTelephony</code> instances in order to apply or
|
||||
* unapply the rules of this policy to the <code>Call</code>s originating
|
||||
* from them.
|
||||
*
|
||||
* @param serviceEvent
|
||||
* a <code>ServiceEvent</code> value which described a change in
|
||||
* a OSGi service and which is to be examined for the registering
|
||||
* or unregistering of a <code>ProtocolProviderService</code> and
|
||||
* thus a <code>OperationSetBasicTelephony</code>
|
||||
*/
|
||||
private void serviceChanged(ServiceEvent serviceEvent)
|
||||
{
|
||||
Object service =
|
||||
bundleContext.getService(serviceEvent.getServiceReference());
|
||||
|
||||
if (service instanceof ProtocolProviderService)
|
||||
{
|
||||
OperationSetBasicTelephony telephony =
|
||||
(OperationSetBasicTelephony) ((ProtocolProviderService) service)
|
||||
.getOperationSet(OperationSetBasicTelephony.class);
|
||||
|
||||
if (telephony != null)
|
||||
{
|
||||
switch (serviceEvent.getType())
|
||||
{
|
||||
case ServiceEvent.REGISTERED:
|
||||
addOperationSetBasicTelephonyListener(telephony);
|
||||
break;
|
||||
case ServiceEvent.UNREGISTERING:
|
||||
removeOperationSetBasicTelephonyListener(telephony);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.impl.protocol;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.osgi.framework.*;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.service.protocol.event.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
/**
|
||||
* Imposes the policy to have one call in progress i.e. to put existing calls on
|
||||
* hold when a new call enters in progress.
|
||||
*
|
||||
* @author Lubomir Marinov
|
||||
*/
|
||||
public class SingleCallInProgressPolicy
|
||||
{
|
||||
|
||||
/**
|
||||
* The name of the configuration property which specifies whether
|
||||
* <code>SingleCallInProgressPolicy</code> is enabled i.e. whether it should
|
||||
* put existing calls on hold when a new call enters in progress.
|
||||
*/
|
||||
private static final String PNAME_SINGLE_CALL_IN_PROGRESS_POLICY_ENABLED
|
||||
= "net.java.sip.communicator.impl.protocol.SingleCallInProgressPolicy.enabled";
|
||||
|
||||
/**
|
||||
* Implements the listeners interfaces used by this policy.
|
||||
*/
|
||||
private class SingleCallInProgressPolicyListener
|
||||
implements CallChangeListener,
|
||||
CallListener,
|
||||
ServiceListener
|
||||
{
|
||||
/**
|
||||
* Stops tracking the state of a specific <code>Call</code> and no
|
||||
* longer tries to put it on hold when it ends.
|
||||
*
|
||||
* @see CallListener#callEnded(CallEvent)
|
||||
*/
|
||||
public void callEnded(CallEvent callEvent)
|
||||
{
|
||||
SingleCallInProgressPolicy.this.handleCallEvent(
|
||||
CallEvent.CALL_ENDED, callEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing because adding <code>CallPeer<code>s to
|
||||
* <code>Call</code>s isn't related to the policy to put existing calls
|
||||
* on hold when a new call becomes in-progress and just implements
|
||||
* <code>CallChangeListener</code>.
|
||||
*
|
||||
* @see CallChangeListener#callPeerAdded(CallPeerEvent)
|
||||
*/
|
||||
public void callPeerAdded( CallPeerEvent callPeerEvent)
|
||||
{
|
||||
|
||||
/*
|
||||
* Not of interest, just implementing CallChangeListener in which
|
||||
* only #callStateChanged(CallChangeEvent) is of interest.
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing because removing <code>CallPeer<code>s to
|
||||
* <code>Call</code>s isn't related to the policy to put existing calls
|
||||
* on hold when a new call becomes in-progress and just implements
|
||||
* <code>CallChangeListener</code>.
|
||||
*
|
||||
* @see CallChangeListener#callPeerRemoved(CallPeerEvent)
|
||||
*/
|
||||
public void callPeerRemoved( CallPeerEvent callPeerEvent)
|
||||
{
|
||||
|
||||
/*
|
||||
* Not of interest, just implementing CallChangeListener in which
|
||||
* only #callStateChanged(CallChangeEvent) is of interest.
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Upon a <code>Call</code> changing its state to
|
||||
* <code>CallState.CALL_IN_PROGRESS</code>, puts the other existing
|
||||
* <code>Call</code>s on hold.
|
||||
*
|
||||
* @param callChangeEvent the <tt>CallChangeEvent</tt> that we are to
|
||||
* deliver.
|
||||
*
|
||||
* @see CallChangeListener#callStateChanged(CallChangeEvent)
|
||||
*/
|
||||
public void callStateChanged(CallChangeEvent callChangeEvent)
|
||||
{
|
||||
SingleCallInProgressPolicy.this.callStateChanged(callChangeEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remembers an incoming <code>Call</code> so that it can put the other
|
||||
* existing <code>Call</code>s on hold when it changes its state to
|
||||
* <code>CallState.CALL_IN_PROGRESS</code>.
|
||||
*
|
||||
* @see CallListener#incomingCallReceived(CallEvent)
|
||||
*/
|
||||
public void incomingCallReceived(CallEvent callEvent)
|
||||
{
|
||||
SingleCallInProgressPolicy.this.handleCallEvent(
|
||||
CallEvent.CALL_RECEIVED, callEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remembers an outgoing <code>Call</code> so that it can put the other
|
||||
* existing <code>Call</code>s on hold when it changes its state to
|
||||
* <code>CallState.CALL_IN_PROGRESS</code>.
|
||||
*
|
||||
* @see CallListener#outgoingCallCreated(CallEvent)
|
||||
*/
|
||||
public void outgoingCallCreated(CallEvent callEvent)
|
||||
{
|
||||
SingleCallInProgressPolicy.this.handleCallEvent(
|
||||
CallEvent.CALL_INITIATED, callEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts/stops tracking the new <code>Call</code>s originating from a
|
||||
* specific <code>ProtocolProviderService</code> when it
|
||||
* registers/unregisters in order to take them into account when putting
|
||||
* existing calls on hold upon a new call entering its in-progress
|
||||
* state.
|
||||
*
|
||||
* @param serviceEvent
|
||||
* the <code>ServiceEvent</code> event describing a change in
|
||||
* the state of a service registration which may be a
|
||||
* <code>ProtocolProviderService</code> supporting
|
||||
* <code>OperationSetBasicTelephony</code> and thus being
|
||||
* able to create new <code>Call</code>s
|
||||
*/
|
||||
public void serviceChanged(ServiceEvent serviceEvent)
|
||||
{
|
||||
SingleCallInProgressPolicy.this.serviceChanged(serviceEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Our class logger
|
||||
*/
|
||||
private static final Logger logger =
|
||||
Logger.getLogger(SingleCallInProgressPolicy.class);
|
||||
|
||||
/**
|
||||
* The <code>BundleContext</code> to the Calls of which this policy applies.
|
||||
*/
|
||||
private final BundleContext bundleContext;
|
||||
|
||||
/**
|
||||
* The <code>Call</code>s this policy manages i.e. put on hold when one of
|
||||
* them enters in progress.
|
||||
*/
|
||||
private final List<Call> calls = new ArrayList<Call>();
|
||||
|
||||
/**
|
||||
* The listener utilized by this policy to discover new <code>Call</code>
|
||||
* and track their in-progress state.
|
||||
*/
|
||||
private final SingleCallInProgressPolicyListener listener =
|
||||
new SingleCallInProgressPolicyListener();
|
||||
|
||||
/**
|
||||
* Initializes a new <code>SingleCallInProgressPolicy</code> instance which
|
||||
* will apply to the <code>Call</code>s of a specific
|
||||
* <code>BundleContext</code>.
|
||||
*
|
||||
* @param bundleContext
|
||||
* the <code>BundleContext</code> to the
|
||||
* <code>Call<code>s of which the new policy should apply
|
||||
*/
|
||||
public SingleCallInProgressPolicy(BundleContext bundleContext)
|
||||
{
|
||||
this.bundleContext = bundleContext;
|
||||
|
||||
this.bundleContext.addServiceListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a specific <code>Call</code> with this policy in order to have
|
||||
* the rules of the latter apply to the former.
|
||||
*
|
||||
* @param call
|
||||
* the <code>Call</code> to register with this policy in order to
|
||||
* have the rules of the latter apply to the former
|
||||
*/
|
||||
private void addCallListener(Call call)
|
||||
{
|
||||
synchronized (calls)
|
||||
{
|
||||
if (!calls.contains(call))
|
||||
{
|
||||
CallState callState = call.getCallState();
|
||||
|
||||
if ((callState != null)
|
||||
&& !callState.equals(CallState.CALL_ENDED))
|
||||
{
|
||||
calls.add(call);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
call.addCallChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a specific <code>OperationSetBasicTelephony</code> with this
|
||||
* policy in order to have the rules of the latter apply to the
|
||||
* <code>Call</code>s created by the former.
|
||||
*
|
||||
* @param telephony
|
||||
* the <code>OperationSetBasicTelephony</code> to register with
|
||||
* this policy in order to have the rules of the latter apply to
|
||||
* the <code>Call</code>s created by the former
|
||||
*/
|
||||
private void addOperationSetBasicTelephonyListener(
|
||||
OperationSetBasicTelephony telephony)
|
||||
{
|
||||
telephony.addCallListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles changes in the state of a <code>Call</code> this policy applies
|
||||
* to in order to detect when new calls become in-progress and when the
|
||||
* other calls should be put on hold.
|
||||
*
|
||||
* @param callChangeEvent
|
||||
* a <code>CallChangeEvent</code> value which describes the
|
||||
* <code>Call</code> and the change in its state
|
||||
*/
|
||||
private void callStateChanged(CallChangeEvent callChangeEvent)
|
||||
{
|
||||
Call call = callChangeEvent.getSourceCall();
|
||||
|
||||
if (CallState.CALL_INITIALIZATION.equals(callChangeEvent.getOldValue())
|
||||
&& CallState.CALL_IN_PROGRESS.equals(call.getCallState())
|
||||
&& ProtocolProviderActivator
|
||||
.getConfigurationService()
|
||||
.getBoolean(
|
||||
PNAME_SINGLE_CALL_IN_PROGRESS_POLICY_ENABLED,
|
||||
true))
|
||||
{
|
||||
synchronized (calls)
|
||||
{
|
||||
for (Call otherCall : calls)
|
||||
if (!call.equals(otherCall)
|
||||
&& CallState.CALL_IN_PROGRESS
|
||||
.equals(otherCall.getCallState()))
|
||||
putOnHold(otherCall);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs end-of-life cleanup associated with this instance e.g. removes
|
||||
* added listeners.
|
||||
*/
|
||||
public void dispose()
|
||||
{
|
||||
bundleContext.removeServiceListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the start and end of the <code>Call</code>s this policy applies
|
||||
* to in order to have them or stop having them put the other existing calls
|
||||
* on hold when the former change their states to
|
||||
* <code>CallState.CALL_IN_PROGRESS</code>.
|
||||
*
|
||||
* @param type
|
||||
* one of {@link CallEvent#CALL_ENDED},
|
||||
* {@link CallEvent#CALL_INITIATED} and
|
||||
* {@link CallEvent#CALL_RECEIVED} which described the type of
|
||||
* the event to be handled
|
||||
* @param callEvent
|
||||
* a <code>CallEvent</code> value which describes the change and
|
||||
* the <code>Call</code> associated with it
|
||||
*/
|
||||
private void handleCallEvent(int type, CallEvent callEvent)
|
||||
{
|
||||
Call call = callEvent.getSourceCall();
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case CallEvent.CALL_ENDED:
|
||||
removeCallListener(call);
|
||||
break;
|
||||
|
||||
case CallEvent.CALL_INITIATED:
|
||||
case CallEvent.CALL_RECEIVED:
|
||||
addCallListener(call);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the <code>CallPeer</code>s of a specific <code>Call</code> on
|
||||
* hold.
|
||||
*
|
||||
* @param call
|
||||
* the <code>Call</code> the <code>CallPeer</code>s of
|
||||
* which are to be put on hold
|
||||
*/
|
||||
private void putOnHold(Call call)
|
||||
{
|
||||
OperationSetBasicTelephony telephony =
|
||||
call.getProtocolProvider()
|
||||
.getOperationSet(OperationSetBasicTelephony.class);
|
||||
|
||||
if (telephony != null)
|
||||
{
|
||||
for (Iterator<? extends CallPeer> peerIter =
|
||||
call.getCallPeers(); peerIter.hasNext();)
|
||||
{
|
||||
CallPeer peer = peerIter.next();
|
||||
CallPeerState peerState = peer.getState();
|
||||
|
||||
if (!CallPeerState.DISCONNECTED.equals(peerState)
|
||||
&& !CallPeerState.FAILED.equals(peerState)
|
||||
&& !CallPeerState.isOnHold(peerState))
|
||||
{
|
||||
try
|
||||
{
|
||||
telephony.putOnHold(peer);
|
||||
}
|
||||
catch (OperationFailedException ex)
|
||||
{
|
||||
logger.error("Failed to put " + peer
|
||||
+ " on hold.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a specific <code>Call</code> from this policy in order to
|
||||
* have the rules of the latter no longer applied to the former.
|
||||
*
|
||||
* @param call
|
||||
* the <code>Call</code> to unregister from this policy in order
|
||||
* to have the rules of the latter no longer apply to the former
|
||||
*/
|
||||
private void removeCallListener(Call call)
|
||||
{
|
||||
call.removeCallChangeListener(listener);
|
||||
|
||||
synchronized (calls)
|
||||
{
|
||||
calls.remove(call);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a specific <code>OperationSetBasicTelephony</code> from this
|
||||
* policy in order to have the rules of the latter no longer apply to the
|
||||
* <code>Call</code>s created by the former.
|
||||
*
|
||||
* @param telephony
|
||||
* the <code>OperationSetBasicTelephony</code> to unregister from
|
||||
* this policy in order to have the rules of the latter apply to
|
||||
* the <code>Call</code>s created by the former
|
||||
*/
|
||||
private void removeOperationSetBasicTelephonyListener(
|
||||
OperationSetBasicTelephony telephony)
|
||||
{
|
||||
telephony.removeCallListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the registering and unregistering of
|
||||
* <code>OperationSetBasicTelephony</code> instances in order to apply or
|
||||
* unapply the rules of this policy to the <code>Call</code>s originating
|
||||
* from them.
|
||||
*
|
||||
* @param serviceEvent
|
||||
* a <code>ServiceEvent</code> value which described a change in
|
||||
* a OSGi service and which is to be examined for the registering
|
||||
* or unregistering of a <code>ProtocolProviderService</code> and
|
||||
* thus a <code>OperationSetBasicTelephony</code>
|
||||
*/
|
||||
private void serviceChanged(ServiceEvent serviceEvent)
|
||||
{
|
||||
Object service =
|
||||
bundleContext.getService(serviceEvent.getServiceReference());
|
||||
|
||||
if (service instanceof ProtocolProviderService)
|
||||
{
|
||||
OperationSetBasicTelephony telephony =
|
||||
((ProtocolProviderService) service)
|
||||
.getOperationSet(OperationSetBasicTelephony.class);
|
||||
|
||||
if (telephony != null)
|
||||
{
|
||||
switch (serviceEvent.getType())
|
||||
{
|
||||
case ServiceEvent.REGISTERED:
|
||||
addOperationSetBasicTelephonyListener(telephony);
|
||||
break;
|
||||
case ServiceEvent.UNREGISTERING:
|
||||
removeOperationSetBasicTelephonyListener(telephony);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,396 +1,395 @@
|
||||
/*
|
||||
* 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.impl.protocol.facebook;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.service.protocol.event.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
import org.apache.http.*;
|
||||
import org.json.*;
|
||||
|
||||
/**
|
||||
* Adapter for the Facebook protocol. This class works as a bridge between the
|
||||
* Facebook specific classes and the sip communication interfaces. It manages
|
||||
* the lifecycle of the classes responsible of accessing Facebook servers and
|
||||
* also manage the necessary {@link Thread threads} used to update the state
|
||||
* with the latest server changes.
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
* @author Lubomir Marinov
|
||||
* @author Edgar Poce
|
||||
*/
|
||||
public class FacebookAdapter
|
||||
implements FacebookSessionListener
|
||||
{
|
||||
private static Logger logger = Logger.getLogger(FacebookAdapter.class);
|
||||
|
||||
private final OperationSetBasicInstantMessagingFacebookImpl
|
||||
basicInstantMessaging;
|
||||
|
||||
/**
|
||||
* Parent service provider
|
||||
*/
|
||||
private final ProtocolProviderServiceFacebookImpl parentProvider;
|
||||
|
||||
private final OperationSetPersistentPresenceFacebookImpl persistentPresence;
|
||||
|
||||
/**
|
||||
* The Facebook session
|
||||
*/
|
||||
private FacebookSession session;
|
||||
|
||||
private final OperationSetTypingNotificationsFacebookImpl
|
||||
typingNotifications;
|
||||
|
||||
/**
|
||||
* Cache of typing notifications
|
||||
*/
|
||||
private final Map<String, TypingNotificationRecord> typingNotificationRecord
|
||||
= new HashMap<String, TypingNotificationRecord>();
|
||||
|
||||
/**
|
||||
* Adapter for each Facebook Chat account.
|
||||
*
|
||||
* @param parentProvider the parent service provider
|
||||
* @param persistentPresence
|
||||
* @param basicInstantMessaging
|
||||
* @param typingNotifications
|
||||
*/
|
||||
public FacebookAdapter(
|
||||
ProtocolProviderServiceFacebookImpl parentProvider,
|
||||
OperationSetPersistentPresenceFacebookImpl persistentPresence,
|
||||
OperationSetBasicInstantMessagingFacebookImpl basicInstantMessaging,
|
||||
OperationSetTypingNotificationsFacebookImpl typingNotifications)
|
||||
{
|
||||
this.parentProvider = parentProvider;
|
||||
this.persistentPresence = persistentPresence;
|
||||
this.basicInstantMessaging = basicInstantMessaging;
|
||||
this.typingNotifications = typingNotifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the facebook id of this account
|
||||
*
|
||||
* @return the facebook id of this account
|
||||
*/
|
||||
public String getUID()
|
||||
{
|
||||
return this.session.getUid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent service provider
|
||||
*
|
||||
* @return parent service provider
|
||||
*/
|
||||
public ProtocolProviderServiceFacebookImpl getParentProvider()
|
||||
{
|
||||
return parentProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the {@link FacebookSession},<br>
|
||||
* Initializes the {@link Thread}s to update the buddy list and to poll
|
||||
* messages
|
||||
*
|
||||
* @param email
|
||||
* @param password
|
||||
* @return true if the user is logged in
|
||||
* @throws IOException
|
||||
* @throws BrokenFacebookProtocolException
|
||||
*/
|
||||
public synchronized boolean initialize(
|
||||
final String email,
|
||||
final String password)
|
||||
throws OperationFailedException
|
||||
{
|
||||
if (this.session != null && this.session.isLoggedIn())
|
||||
return true;
|
||||
|
||||
logger.info("initializing facebook adapter. account");
|
||||
try
|
||||
{
|
||||
this.session = new FacebookSession();
|
||||
boolean loggedIn = session.login(email, password);
|
||||
if (loggedIn)
|
||||
session.addListener(this);
|
||||
return loggedIn;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw
|
||||
new OperationFailedException(
|
||||
"unable to initialize adapter",
|
||||
FacebookErrorException.kError_Login_GenericError,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post typing notification to the given contact.
|
||||
*
|
||||
* @param notifiedContact
|
||||
* the contact we want to notify
|
||||
* @param typingState
|
||||
* our current typing state(SC)
|
||||
* @throws HttpException
|
||||
* the http exception
|
||||
* @throws IOException
|
||||
* IO exception
|
||||
* @throws JSONException
|
||||
* JSON parsing exception
|
||||
* @throws Exception
|
||||
* the general exception
|
||||
*/
|
||||
public void postTypingNotification(Contact notifiedContact, int typingState)
|
||||
throws HttpException,
|
||||
IOException,
|
||||
JSONException,
|
||||
Exception
|
||||
{
|
||||
TypingNotificationRecord record
|
||||
= typingNotificationRecord.get(notifiedContact.getAddress());
|
||||
if (record == null)
|
||||
{
|
||||
record = new TypingNotificationRecord(-1l, -1);
|
||||
synchronized (typingNotificationRecord)
|
||||
{
|
||||
typingNotificationRecord
|
||||
.put(notifiedContact.getAddress(), record);
|
||||
}
|
||||
}
|
||||
|
||||
if (record.getTime() < System.currentTimeMillis() - 1000)
|
||||
{
|
||||
FacebookOutgoingTypingNotification msg
|
||||
= new FacebookOutgoingTypingNotification(session);
|
||||
msg.setAddress(notifiedContact.getAddress());
|
||||
msg.setTypingState(typingState);
|
||||
msg.send();
|
||||
record.setTime(System.currentTimeMillis());
|
||||
record.setType(typingState);
|
||||
}
|
||||
}
|
||||
|
||||
public void setStatusMessage(String statusMessage)
|
||||
throws OperationFailedException
|
||||
{
|
||||
try
|
||||
{
|
||||
this.session.setStatusMessage(statusMessage);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw
|
||||
new OperationFailedException(
|
||||
"unable to change facebook status message",
|
||||
-1,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void shutdown()
|
||||
{
|
||||
if (session != null)
|
||||
{
|
||||
logger.info("shutting down facebook adapter");
|
||||
session.logout();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param message
|
||||
* @param to
|
||||
* @return
|
||||
*/
|
||||
public synchronized MessageDeliveryFailedEvent postMessage(
|
||||
Message message,
|
||||
Contact to)
|
||||
{
|
||||
FacebookOutgoingMailboxMessage msg
|
||||
= new FacebookOutgoingMailboxMessage(session);
|
||||
msg.setAddress(to.getAddress());
|
||||
msg.setContent(message.getContent());
|
||||
msg.setSubject(message.getSubject());
|
||||
msg.setUid(message.getMessageUID());
|
||||
try
|
||||
{
|
||||
msg.send();
|
||||
return null;
|
||||
}
|
||||
catch (BrokenFacebookProtocolException e)
|
||||
{
|
||||
logger.error(e);
|
||||
return new MessageDeliveryFailedEvent(message, to, -1, System
|
||||
.currentTimeMillis(), e.getMessage());
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
logger.warn(e);
|
||||
return new MessageDeliveryFailedEvent(message, to, -1, System
|
||||
.currentTimeMillis(), e.getMessage());
|
||||
}
|
||||
catch (FacebookErrorException e)
|
||||
{
|
||||
return new MessageDeliveryFailedEvent(message, to, e.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public MessageDeliveryFailedEvent postFacebookChatMessage(
|
||||
Message message,
|
||||
Contact to)
|
||||
{
|
||||
FacebookOutgoingChatMessage msg
|
||||
= new FacebookOutgoingChatMessage(session);
|
||||
msg.setAddress(to.getAddress());
|
||||
msg.setContent(message.getContent());
|
||||
msg.setMessageUid(message.getMessageUID());
|
||||
try
|
||||
{
|
||||
msg.send();
|
||||
return null;
|
||||
}
|
||||
catch (BrokenFacebookProtocolException e)
|
||||
{
|
||||
logger.error(e);
|
||||
return new MessageDeliveryFailedEvent(message, to, -1, System
|
||||
.currentTimeMillis(), e.getMessage());
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
logger.warn(e);
|
||||
return new MessageDeliveryFailedEvent(message, to, -1, System
|
||||
.currentTimeMillis(), e.getMessage());
|
||||
}
|
||||
catch (FacebookErrorException e)
|
||||
{
|
||||
return new MessageDeliveryFailedEvent(message, to, e.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Promotes the incoming message to the GUI
|
||||
*
|
||||
* @see FacebookIncomingMessageListener#onIncomingChatMessage(FacebookMessage)
|
||||
*/
|
||||
public void onIncomingChatMessage(FacebookMessage msg)
|
||||
{
|
||||
if (!msg.getFrom().equals(this.session.getUid()))
|
||||
basicInstantMessaging.receivedInstantMessage(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Promotes the incoming notification to the GUI
|
||||
*
|
||||
* @see FacebookIncomingMessageListener#onIncomingTypingNotification(String,
|
||||
* int)
|
||||
*/
|
||||
public void onIncomingTypingNotification(String buddyUid, int state)
|
||||
{
|
||||
if (!buddyUid.equals(this.session.getUid()))
|
||||
{
|
||||
Contact fromContact = persistentPresence.findContactByID(buddyUid);
|
||||
if (fromContact == null)
|
||||
fromContact
|
||||
= persistentPresence.createVolatileContact(buddyUid);
|
||||
|
||||
int typingState = OperationSetTypingNotifications.STATE_UNKNOWN;
|
||||
switch (state)
|
||||
{
|
||||
case 1:
|
||||
typingState = OperationSetTypingNotifications.STATE_TYPING;
|
||||
break;
|
||||
case 0:
|
||||
typingState = OperationSetTypingNotifications.STATE_STOPPED;
|
||||
break;
|
||||
default:
|
||||
typingState = OperationSetTypingNotifications.STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
typingNotifications
|
||||
.receivedTypingNotification(fromContact, typingState);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* notifies SC that the connection is lost
|
||||
*/
|
||||
public void onFacebookConnectionLost() {
|
||||
if (parentProvider.isRegistered())
|
||||
{
|
||||
try
|
||||
{
|
||||
parentProvider.unregister();
|
||||
}
|
||||
catch (OperationFailedException e)
|
||||
{
|
||||
logger.error("unable to unregister", e);
|
||||
}
|
||||
}
|
||||
|
||||
// tag all the buddies as offline
|
||||
persistentPresence
|
||||
.setPresenceStatusForAllContacts(FacebookStatusEnum.OFFLINE);
|
||||
}
|
||||
|
||||
public void onBuddyListUpdated()
|
||||
{
|
||||
for (FacebookUser user : this.session.getBuddyList().getBuddies())
|
||||
{
|
||||
PresenceStatus newStatus;
|
||||
if (user.isOnline && user.isIdle)
|
||||
newStatus = FacebookStatusEnum.IDLE;
|
||||
else if (user.isOnline)
|
||||
newStatus = FacebookStatusEnum.ONLINE;
|
||||
else
|
||||
newStatus = FacebookStatusEnum.OFFLINE;
|
||||
|
||||
persistentPresence.setPresenceStatusForContact(user.uid, newStatus);
|
||||
}
|
||||
}
|
||||
|
||||
public FacebookSession getSession()
|
||||
{
|
||||
return session;
|
||||
}
|
||||
|
||||
private static class TypingNotificationRecord
|
||||
{
|
||||
private long time;
|
||||
private int type;
|
||||
|
||||
public TypingNotificationRecord(long time, int type)
|
||||
{
|
||||
this.time = time;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public long getTime()
|
||||
{
|
||||
return time;
|
||||
}
|
||||
|
||||
public int getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setTime(long time)
|
||||
{
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public void setType(int type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.impl.protocol.facebook;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.service.protocol.event.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
import org.apache.http.*;
|
||||
import org.json.*;
|
||||
|
||||
/**
|
||||
* Adapter for the Facebook protocol. This class works as a bridge between the
|
||||
* Facebook specific classes and the sip communication interfaces. It manages
|
||||
* the lifecycle of the classes responsible of accessing Facebook servers and
|
||||
* also manage the necessary {@link Thread threads} used to update the state
|
||||
* with the latest server changes.
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
* @author Lubomir Marinov
|
||||
* @author Edgar Poce
|
||||
*/
|
||||
public class FacebookAdapter
|
||||
implements FacebookSessionListener
|
||||
{
|
||||
private static Logger logger = Logger.getLogger(FacebookAdapter.class);
|
||||
|
||||
private final OperationSetBasicInstantMessagingFacebookImpl
|
||||
basicInstantMessaging;
|
||||
|
||||
/**
|
||||
* Parent service provider
|
||||
*/
|
||||
private final ProtocolProviderServiceFacebookImpl parentProvider;
|
||||
|
||||
private final OperationSetPersistentPresenceFacebookImpl persistentPresence;
|
||||
|
||||
/**
|
||||
* The Facebook session
|
||||
*/
|
||||
private FacebookSession session;
|
||||
|
||||
private final OperationSetTypingNotificationsFacebookImpl
|
||||
typingNotifications;
|
||||
|
||||
/**
|
||||
* Cache of typing notifications
|
||||
*/
|
||||
private final Map<String, TypingNotificationRecord> typingNotificationRecord
|
||||
= new HashMap<String, TypingNotificationRecord>();
|
||||
|
||||
/**
|
||||
* Adapter for each Facebook Chat account.
|
||||
*
|
||||
* @param parentProvider the parent service provider
|
||||
* @param persistentPresence
|
||||
* @param basicInstantMessaging
|
||||
* @param typingNotifications
|
||||
*/
|
||||
public FacebookAdapter(
|
||||
ProtocolProviderServiceFacebookImpl parentProvider,
|
||||
OperationSetPersistentPresenceFacebookImpl persistentPresence,
|
||||
OperationSetBasicInstantMessagingFacebookImpl basicInstantMessaging,
|
||||
OperationSetTypingNotificationsFacebookImpl typingNotifications)
|
||||
{
|
||||
this.parentProvider = parentProvider;
|
||||
this.persistentPresence = persistentPresence;
|
||||
this.basicInstantMessaging = basicInstantMessaging;
|
||||
this.typingNotifications = typingNotifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the facebook id of this account
|
||||
*
|
||||
* @return the facebook id of this account
|
||||
*/
|
||||
public String getUID()
|
||||
{
|
||||
return this.session.getUid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent service provider
|
||||
*
|
||||
* @return parent service provider
|
||||
*/
|
||||
public ProtocolProviderServiceFacebookImpl getParentProvider()
|
||||
{
|
||||
return parentProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the {@link FacebookSession},<br>
|
||||
* Initializes the {@link Thread}s to update the buddy list and to poll
|
||||
* messages
|
||||
*
|
||||
* @param email
|
||||
* @param password
|
||||
* @return true if the user is logged in
|
||||
* @throws IOException
|
||||
* @throws BrokenFacebookProtocolException
|
||||
*/
|
||||
public synchronized boolean initialize(
|
||||
final String email,
|
||||
final String password)
|
||||
throws OperationFailedException
|
||||
{
|
||||
if (this.session != null && this.session.isLoggedIn())
|
||||
return true;
|
||||
|
||||
logger.info("initializing facebook adapter. account");
|
||||
try
|
||||
{
|
||||
this.session = new FacebookSession();
|
||||
boolean loggedIn = session.login(email, password);
|
||||
if (loggedIn)
|
||||
session.addListener(this);
|
||||
return loggedIn;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw
|
||||
new OperationFailedException(
|
||||
"unable to initialize adapter",
|
||||
FacebookErrorException.kError_Login_GenericError,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post typing notification to the given contact.
|
||||
*
|
||||
* @param notifiedContact
|
||||
* the contact we want to notify
|
||||
* @param typingState
|
||||
* our current typing state(SC)
|
||||
* @throws HttpException
|
||||
* the http exception
|
||||
* @throws IOException
|
||||
* IO exception
|
||||
* @throws JSONException
|
||||
* JSON parsing exception
|
||||
* @throws Exception
|
||||
* the general exception
|
||||
*/
|
||||
public void postTypingNotification(Contact notifiedContact, int typingState)
|
||||
throws HttpException,
|
||||
IOException,
|
||||
JSONException,
|
||||
Exception
|
||||
{
|
||||
TypingNotificationRecord record
|
||||
= typingNotificationRecord.get(notifiedContact.getAddress());
|
||||
if (record == null)
|
||||
{
|
||||
record = new TypingNotificationRecord(-1l, -1);
|
||||
synchronized (typingNotificationRecord)
|
||||
{
|
||||
typingNotificationRecord
|
||||
.put(notifiedContact.getAddress(), record);
|
||||
}
|
||||
}
|
||||
|
||||
if (record.getTime() < System.currentTimeMillis() - 1000)
|
||||
{
|
||||
FacebookOutgoingTypingNotification msg
|
||||
= new FacebookOutgoingTypingNotification(session);
|
||||
msg.setAddress(notifiedContact.getAddress());
|
||||
msg.setTypingState(typingState);
|
||||
msg.send();
|
||||
record.setTime(System.currentTimeMillis());
|
||||
record.setType(typingState);
|
||||
}
|
||||
}
|
||||
|
||||
public void setStatusMessage(String statusMessage)
|
||||
throws OperationFailedException
|
||||
{
|
||||
try
|
||||
{
|
||||
this.session.setStatusMessage(statusMessage);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw
|
||||
new OperationFailedException(
|
||||
"unable to change facebook status message",
|
||||
-1,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void shutdown()
|
||||
{
|
||||
if (session != null)
|
||||
{
|
||||
logger.info("shutting down facebook adapter");
|
||||
session.logout();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param message
|
||||
* @param to
|
||||
* @return
|
||||
*/
|
||||
public synchronized MessageDeliveryFailedEvent postMessage(
|
||||
Message message,
|
||||
Contact to)
|
||||
{
|
||||
FacebookOutgoingMailboxMessage msg
|
||||
= new FacebookOutgoingMailboxMessage(session);
|
||||
msg.setAddress(to.getAddress());
|
||||
msg.setContent(message.getContent());
|
||||
msg.setSubject(message.getSubject());
|
||||
msg.setUid(message.getMessageUID());
|
||||
try
|
||||
{
|
||||
msg.send();
|
||||
return null;
|
||||
}
|
||||
catch (BrokenFacebookProtocolException e)
|
||||
{
|
||||
logger.error(e);
|
||||
return new MessageDeliveryFailedEvent(message, to, -1, System
|
||||
.currentTimeMillis(), e.getMessage());
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
logger.warn(e);
|
||||
return new MessageDeliveryFailedEvent(message, to, -1, System
|
||||
.currentTimeMillis(), e.getMessage());
|
||||
}
|
||||
catch (FacebookErrorException e)
|
||||
{
|
||||
return new MessageDeliveryFailedEvent(message, to, e.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public MessageDeliveryFailedEvent postFacebookChatMessage(
|
||||
Message message,
|
||||
Contact to)
|
||||
{
|
||||
FacebookOutgoingChatMessage msg
|
||||
= new FacebookOutgoingChatMessage(session);
|
||||
msg.setAddress(to.getAddress());
|
||||
msg.setContent(message.getContent());
|
||||
msg.setMessageUid(message.getMessageUID());
|
||||
try
|
||||
{
|
||||
msg.send();
|
||||
return null;
|
||||
}
|
||||
catch (BrokenFacebookProtocolException e)
|
||||
{
|
||||
logger.error(e);
|
||||
return new MessageDeliveryFailedEvent(message, to, -1, System
|
||||
.currentTimeMillis(), e.getMessage());
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
logger.warn(e);
|
||||
return new MessageDeliveryFailedEvent(message, to, -1, System
|
||||
.currentTimeMillis(), e.getMessage());
|
||||
}
|
||||
catch (FacebookErrorException e)
|
||||
{
|
||||
return new MessageDeliveryFailedEvent(message, to, e.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Promotes the incoming message to the GUI
|
||||
*
|
||||
* @see FacebookSessionListener#onIncomingChatMessage(FacebookMessage)
|
||||
*/
|
||||
public void onIncomingChatMessage(FacebookMessage msg)
|
||||
{
|
||||
if (!msg.getFrom().equals(this.session.getUid()))
|
||||
basicInstantMessaging.receivedInstantMessage(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Promotes the incoming notification to the GUI
|
||||
*
|
||||
* @see FacebookSessionListener#onIncomingTypingNotification(String, int)
|
||||
*/
|
||||
public void onIncomingTypingNotification(String buddyUid, int state)
|
||||
{
|
||||
if (!buddyUid.equals(this.session.getUid()))
|
||||
{
|
||||
Contact fromContact = persistentPresence.findContactByID(buddyUid);
|
||||
if (fromContact == null)
|
||||
fromContact
|
||||
= persistentPresence.createVolatileContact(buddyUid);
|
||||
|
||||
int typingState = OperationSetTypingNotifications.STATE_UNKNOWN;
|
||||
switch (state)
|
||||
{
|
||||
case 1:
|
||||
typingState = OperationSetTypingNotifications.STATE_TYPING;
|
||||
break;
|
||||
case 0:
|
||||
typingState = OperationSetTypingNotifications.STATE_STOPPED;
|
||||
break;
|
||||
default:
|
||||
typingState = OperationSetTypingNotifications.STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
typingNotifications
|
||||
.receivedTypingNotification(fromContact, typingState);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* notifies SC that the connection is lost
|
||||
*/
|
||||
public void onFacebookConnectionLost() {
|
||||
if (parentProvider.isRegistered())
|
||||
{
|
||||
try
|
||||
{
|
||||
parentProvider.unregister();
|
||||
}
|
||||
catch (OperationFailedException e)
|
||||
{
|
||||
logger.error("unable to unregister", e);
|
||||
}
|
||||
}
|
||||
|
||||
// tag all the buddies as offline
|
||||
persistentPresence
|
||||
.setPresenceStatusForAllContacts(FacebookStatusEnum.OFFLINE);
|
||||
}
|
||||
|
||||
public void onBuddyListUpdated()
|
||||
{
|
||||
for (FacebookUser user : this.session.getBuddyList().getBuddies())
|
||||
{
|
||||
PresenceStatus newStatus;
|
||||
if (user.isOnline && user.isIdle)
|
||||
newStatus = FacebookStatusEnum.IDLE;
|
||||
else if (user.isOnline)
|
||||
newStatus = FacebookStatusEnum.ONLINE;
|
||||
else
|
||||
newStatus = FacebookStatusEnum.OFFLINE;
|
||||
|
||||
persistentPresence.setPresenceStatusForContact(user.uid, newStatus);
|
||||
}
|
||||
}
|
||||
|
||||
public FacebookSession getSession()
|
||||
{
|
||||
return session;
|
||||
}
|
||||
|
||||
private static class TypingNotificationRecord
|
||||
{
|
||||
private long time;
|
||||
private int type;
|
||||
|
||||
public TypingNotificationRecord(long time, int type)
|
||||
{
|
||||
this.time = time;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public long getTime()
|
||||
{
|
||||
return time;
|
||||
}
|
||||
|
||||
public int getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setTime(long time)
|
||||
{
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public void setType(int type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,288 +1,287 @@
|
||||
/*
|
||||
* 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.impl.protocol.facebook;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
import org.apache.http.*;
|
||||
import org.apache.http.client.entity.*;
|
||||
import org.apache.http.client.methods.*;
|
||||
import org.apache.http.message.*;
|
||||
import org.apache.http.protocol.HTTP;
|
||||
import org.apache.http.util.*;
|
||||
import org.json.*;
|
||||
|
||||
/**
|
||||
* Facebook buddy list that store the online buddies information we got from the
|
||||
* server since we logged in. Some information of ourselves also included. <br>
|
||||
* This class is responsible of establishing the http connections and updating
|
||||
* its state when requested.
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
* @author Edgar Poce
|
||||
*/
|
||||
public class FacebookBuddyList
|
||||
{
|
||||
private static Logger logger = Logger.getLogger(FacebookBuddyList.class);
|
||||
|
||||
/**
|
||||
* The url of the update
|
||||
*/
|
||||
private static final String BUDDYLIST_URL
|
||||
= "http://www.facebook.com/ajax/chat/buddy_list.php";
|
||||
|
||||
/**
|
||||
* The Facebook Session
|
||||
*/
|
||||
private final FacebookSession session;
|
||||
|
||||
/**
|
||||
* Our (online) buddies' information cache
|
||||
*/
|
||||
private transient Map<String, FacebookUser> cache
|
||||
= new LinkedHashMap<String, FacebookUser>();
|
||||
|
||||
/**
|
||||
* Some information of ourselves/myself.
|
||||
*/
|
||||
private transient FacebookUser me;
|
||||
|
||||
/**
|
||||
* Listener of this buddy list
|
||||
*/
|
||||
private List<FacebookSessionListener> listeners
|
||||
= new ArrayList<FacebookSessionListener>();
|
||||
|
||||
/**
|
||||
* Init the cache and the parent adapter.
|
||||
*
|
||||
* @param adapter
|
||||
*/
|
||||
public FacebookBuddyList(FacebookSession session)
|
||||
{
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public void update()
|
||||
throws BrokenFacebookProtocolException,
|
||||
IOException,
|
||||
FacebookErrorException
|
||||
{
|
||||
// reconnecting
|
||||
this.session.reconnect();
|
||||
logger.info("Updating buddy list...");
|
||||
// perform POST request
|
||||
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
|
||||
nvps.add(new BasicNameValuePair("buddy_list", "1"));
|
||||
nvps.add(new BasicNameValuePair("notifications", "1"));
|
||||
nvps.add(new BasicNameValuePair("force_render", "true"));
|
||||
nvps.add(new BasicNameValuePair("post_form_id", session.getFormId()));
|
||||
nvps.add(new BasicNameValuePair("user", session.getUid()));
|
||||
HttpPost post = new HttpPost(BUDDYLIST_URL);
|
||||
post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
|
||||
HttpResponse response = this.session.getHttpClient().execute(post);
|
||||
String body = EntityUtils.toString(response.getEntity());
|
||||
if (response.getStatusLine().getStatusCode() != 200)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException("Code "
|
||||
+ response.getStatusLine().getStatusCode()
|
||||
+ ". Unable to update Facebook buddy list.");
|
||||
}
|
||||
this.update(body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get meta info of this account
|
||||
*
|
||||
* @return meta info of this account
|
||||
*/
|
||||
public FacebookUser getMyMetaInfo()
|
||||
{
|
||||
return me;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get our buddy who has the given id from the cache.
|
||||
*
|
||||
* @param contactID
|
||||
* the id we wanna look up
|
||||
* @return the buddy who has the given id
|
||||
* @throws FacebookErrorException
|
||||
* @throws IOException
|
||||
* @throws BrokenFacebookProtocolException
|
||||
*/
|
||||
public FacebookUser getBuddyByUID(String uid)
|
||||
throws BrokenFacebookProtocolException,
|
||||
IOException,
|
||||
FacebookErrorException
|
||||
{
|
||||
FacebookUser buddy = this.cache.get(uid);
|
||||
if (buddy == null && this.me != null && this.me.uid.equals(uid))
|
||||
{
|
||||
buddy = me;
|
||||
}
|
||||
return buddy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the users in the cache
|
||||
*/
|
||||
public Collection<FacebookUser> getBuddies()
|
||||
{
|
||||
return Collections.unmodifiableCollection(this.cache.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the resource
|
||||
*/
|
||||
public void clear()
|
||||
{
|
||||
for (FacebookUser u : this.cache.values())
|
||||
{
|
||||
u.isOnline = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the buddy list<br>
|
||||
* If the {@link FacebookSession} is logged out then this method returns
|
||||
* without modifying the state of the buddy list.
|
||||
*
|
||||
* @param body
|
||||
* @throws BrokenFacebookProtocolException
|
||||
* @throws FacebookErrorException
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void update(String body)
|
||||
throws BrokenFacebookProtocolException,
|
||||
FacebookErrorException
|
||||
{
|
||||
// the session might be getting closed by another thread
|
||||
// do nothing if it's logged out
|
||||
synchronized (session)
|
||||
{
|
||||
if (!session.isLoggedIn())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject jsonBuddyList = parseBody(body);
|
||||
try {
|
||||
/*
|
||||
* If listChanged, then we can get the buddies available via looking
|
||||
* at the nowAvailableList else. we can only get the buddies' info,
|
||||
* and the nowAvailableList is empty.
|
||||
*/
|
||||
JSONObject userInfos = (JSONObject) jsonBuddyList.get("userInfos");
|
||||
if (userInfos != null)
|
||||
{
|
||||
// Then add the new buddies and set them as online(constructor)
|
||||
Iterator<String> it = userInfos.keys();
|
||||
while (it.hasNext())
|
||||
{
|
||||
String key = it.next();
|
||||
JSONObject jsonUser = (JSONObject) userInfos.get(key);
|
||||
if (jsonUser == null)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(
|
||||
"unable to get user info. " + userInfos);
|
||||
}
|
||||
FacebookUser buddy = new FacebookUser(key, jsonUser);
|
||||
if (buddy.uid.equals(this.session.getUid()))
|
||||
{
|
||||
this.me = buddy;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.cache.put(key, buddy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject nowAvailableList = jsonBuddyList
|
||||
.getJSONObject("nowAvailableList");
|
||||
|
||||
if (nowAvailableList == null)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(
|
||||
"Unable to read Facebook now available list");
|
||||
}
|
||||
|
||||
for (FacebookUser user : this.cache.values())
|
||||
{
|
||||
if (nowAvailableList.has(user.uid))
|
||||
{
|
||||
user.isOnline = true;
|
||||
user.lastSeen = Calendar.getInstance().getTimeInMillis();
|
||||
user.isIdle = nowAvailableList.getJSONObject(user.uid)
|
||||
.getBoolean("i");
|
||||
}
|
||||
else
|
||||
{
|
||||
user.isOnline = false;
|
||||
}
|
||||
}
|
||||
// notify listeners
|
||||
for (FacebookSessionListener l : this.listeners)
|
||||
l.onBuddyListUpdated();
|
||||
}
|
||||
catch (JSONException e)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private JSONObject parseBody(String body)
|
||||
throws BrokenFacebookProtocolException,
|
||||
FacebookErrorException
|
||||
{
|
||||
FacebookJsonResponse jsonResponse = new FacebookJsonResponse(session,
|
||||
body);
|
||||
try
|
||||
{
|
||||
JSONObject json = jsonResponse.getJson();
|
||||
JSONObject payload = (JSONObject) json.get("payload");
|
||||
if (payload == null)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(
|
||||
"unable to parse buddy list. there's no payload field. "
|
||||
+ jsonResponse);
|
||||
}
|
||||
JSONObject jsonBuddyList = (JSONObject) payload.get("buddy_list");
|
||||
if (jsonBuddyList == null)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(
|
||||
"unable to parse buddy list. there's no buddy list field. "
|
||||
+ jsonResponse);
|
||||
}
|
||||
return jsonBuddyList;
|
||||
}
|
||||
catch (JSONException e)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(
|
||||
"unable to parse json response");
|
||||
}
|
||||
}
|
||||
|
||||
public int getSize()
|
||||
{
|
||||
return this.cache.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener which is notified when the buddy list state changes
|
||||
*
|
||||
* @param listener
|
||||
*/
|
||||
public void addListener(FacebookSessionListener listener)
|
||||
{
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.impl.protocol.facebook;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
import org.apache.http.*;
|
||||
import org.apache.http.client.entity.*;
|
||||
import org.apache.http.client.methods.*;
|
||||
import org.apache.http.message.*;
|
||||
import org.apache.http.protocol.HTTP;
|
||||
import org.apache.http.util.*;
|
||||
import org.json.*;
|
||||
|
||||
/**
|
||||
* Facebook buddy list that store the online buddies information we got from the
|
||||
* server since we logged in. Some information of ourselves also included. <br>
|
||||
* This class is responsible of establishing the http connections and updating
|
||||
* its state when requested.
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
* @author Edgar Poce
|
||||
*/
|
||||
public class FacebookBuddyList
|
||||
{
|
||||
private static Logger logger = Logger.getLogger(FacebookBuddyList.class);
|
||||
|
||||
/**
|
||||
* The url of the update
|
||||
*/
|
||||
private static final String BUDDYLIST_URL
|
||||
= "http://www.facebook.com/ajax/chat/buddy_list.php";
|
||||
|
||||
/**
|
||||
* The Facebook Session
|
||||
*/
|
||||
private final FacebookSession session;
|
||||
|
||||
/**
|
||||
* Our (online) buddies' information cache
|
||||
*/
|
||||
private transient Map<String, FacebookUser> cache
|
||||
= new LinkedHashMap<String, FacebookUser>();
|
||||
|
||||
/**
|
||||
* Some information of ourselves/myself.
|
||||
*/
|
||||
private transient FacebookUser me;
|
||||
|
||||
/**
|
||||
* Listener of this buddy list
|
||||
*/
|
||||
private List<FacebookSessionListener> listeners
|
||||
= new ArrayList<FacebookSessionListener>();
|
||||
|
||||
/**
|
||||
* Init the cache and the parent adapter.
|
||||
*
|
||||
* @param session the Facebook session
|
||||
*/
|
||||
public FacebookBuddyList(FacebookSession session)
|
||||
{
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public void update()
|
||||
throws BrokenFacebookProtocolException,
|
||||
IOException,
|
||||
FacebookErrorException
|
||||
{
|
||||
// reconnecting
|
||||
this.session.reconnect();
|
||||
logger.info("Updating buddy list...");
|
||||
// perform POST request
|
||||
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
|
||||
nvps.add(new BasicNameValuePair("buddy_list", "1"));
|
||||
nvps.add(new BasicNameValuePair("notifications", "1"));
|
||||
nvps.add(new BasicNameValuePair("force_render", "true"));
|
||||
nvps.add(new BasicNameValuePair("post_form_id", session.getFormId()));
|
||||
nvps.add(new BasicNameValuePair("user", session.getUid()));
|
||||
HttpPost post = new HttpPost(BUDDYLIST_URL);
|
||||
post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
|
||||
HttpResponse response = this.session.getHttpClient().execute(post);
|
||||
String body = EntityUtils.toString(response.getEntity());
|
||||
if (response.getStatusLine().getStatusCode() != 200)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException("Code "
|
||||
+ response.getStatusLine().getStatusCode()
|
||||
+ ". Unable to update Facebook buddy list.");
|
||||
}
|
||||
this.update(body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get meta info of this account
|
||||
*
|
||||
* @return meta info of this account
|
||||
*/
|
||||
public FacebookUser getMyMetaInfo()
|
||||
{
|
||||
return me;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get our buddy who has the given id from the cache.
|
||||
*
|
||||
* @param uid the id we want to look up
|
||||
* @return the buddy who has the given id
|
||||
* @throws FacebookErrorException
|
||||
* @throws IOException
|
||||
* @throws BrokenFacebookProtocolException
|
||||
*/
|
||||
public FacebookUser getBuddyByUID(String uid)
|
||||
throws BrokenFacebookProtocolException,
|
||||
IOException,
|
||||
FacebookErrorException
|
||||
{
|
||||
FacebookUser buddy = this.cache.get(uid);
|
||||
if (buddy == null && this.me != null && this.me.uid.equals(uid))
|
||||
{
|
||||
buddy = me;
|
||||
}
|
||||
return buddy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the users in the cache
|
||||
*/
|
||||
public Collection<FacebookUser> getBuddies()
|
||||
{
|
||||
return Collections.unmodifiableCollection(this.cache.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the resource
|
||||
*/
|
||||
public void clear()
|
||||
{
|
||||
for (FacebookUser u : this.cache.values())
|
||||
{
|
||||
u.isOnline = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the buddy list<br>
|
||||
* If the {@link FacebookSession} is logged out then this method returns
|
||||
* without modifying the state of the buddy list.
|
||||
*
|
||||
* @param body
|
||||
* @throws BrokenFacebookProtocolException
|
||||
* @throws FacebookErrorException
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void update(String body)
|
||||
throws BrokenFacebookProtocolException,
|
||||
FacebookErrorException
|
||||
{
|
||||
// the session might be getting closed by another thread
|
||||
// do nothing if it's logged out
|
||||
synchronized (session)
|
||||
{
|
||||
if (!session.isLoggedIn())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject jsonBuddyList = parseBody(body);
|
||||
try {
|
||||
/*
|
||||
* If listChanged, then we can get the buddies available via looking
|
||||
* at the nowAvailableList else. we can only get the buddies' info,
|
||||
* and the nowAvailableList is empty.
|
||||
*/
|
||||
JSONObject userInfos = (JSONObject) jsonBuddyList.get("userInfos");
|
||||
if (userInfos != null)
|
||||
{
|
||||
// Then add the new buddies and set them as online(constructor)
|
||||
Iterator<String> it = userInfos.keys();
|
||||
while (it.hasNext())
|
||||
{
|
||||
String key = it.next();
|
||||
JSONObject jsonUser = (JSONObject) userInfos.get(key);
|
||||
if (jsonUser == null)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(
|
||||
"unable to get user info. " + userInfos);
|
||||
}
|
||||
FacebookUser buddy = new FacebookUser(key, jsonUser);
|
||||
if (buddy.uid.equals(this.session.getUid()))
|
||||
{
|
||||
this.me = buddy;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.cache.put(key, buddy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject nowAvailableList = jsonBuddyList
|
||||
.getJSONObject("nowAvailableList");
|
||||
|
||||
if (nowAvailableList == null)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(
|
||||
"Unable to read Facebook now available list");
|
||||
}
|
||||
|
||||
for (FacebookUser user : this.cache.values())
|
||||
{
|
||||
if (nowAvailableList.has(user.uid))
|
||||
{
|
||||
user.isOnline = true;
|
||||
user.lastSeen = Calendar.getInstance().getTimeInMillis();
|
||||
user.isIdle = nowAvailableList.getJSONObject(user.uid)
|
||||
.getBoolean("i");
|
||||
}
|
||||
else
|
||||
{
|
||||
user.isOnline = false;
|
||||
}
|
||||
}
|
||||
// notify listeners
|
||||
for (FacebookSessionListener l : this.listeners)
|
||||
l.onBuddyListUpdated();
|
||||
}
|
||||
catch (JSONException e)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private JSONObject parseBody(String body)
|
||||
throws BrokenFacebookProtocolException,
|
||||
FacebookErrorException
|
||||
{
|
||||
FacebookJsonResponse jsonResponse = new FacebookJsonResponse(session,
|
||||
body);
|
||||
try
|
||||
{
|
||||
JSONObject json = jsonResponse.getJson();
|
||||
JSONObject payload = (JSONObject) json.get("payload");
|
||||
if (payload == null)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(
|
||||
"unable to parse buddy list. there's no payload field. "
|
||||
+ jsonResponse);
|
||||
}
|
||||
JSONObject jsonBuddyList = (JSONObject) payload.get("buddy_list");
|
||||
if (jsonBuddyList == null)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(
|
||||
"unable to parse buddy list. there's no buddy list field. "
|
||||
+ jsonResponse);
|
||||
}
|
||||
return jsonBuddyList;
|
||||
}
|
||||
catch (JSONException e)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(
|
||||
"unable to parse json response");
|
||||
}
|
||||
}
|
||||
|
||||
public int getSize()
|
||||
{
|
||||
return this.cache.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener which is notified when the buddy list state changes
|
||||
*
|
||||
* @param listener
|
||||
*/
|
||||
public void addListener(FacebookSessionListener listener)
|
||||
{
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,266 +1,265 @@
|
||||
/*
|
||||
* 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.impl.protocol.facebook;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.service.protocol.event.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
/**
|
||||
* Instant messaging functionality for the Facebook protocol.
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
* @author Lubomir Marinov
|
||||
*/
|
||||
public class OperationSetBasicInstantMessagingFacebookImpl
|
||||
extends AbstractOperationSetBasicInstantMessaging
|
||||
{
|
||||
private static final Logger logger
|
||||
= Logger.getLogger(OperationSetBasicInstantMessagingFacebookImpl.class);
|
||||
|
||||
/**
|
||||
* The currently valid persistent presence operation set..
|
||||
*/
|
||||
private OperationSetPersistentPresenceFacebookImpl opSetPersPresence = null;
|
||||
|
||||
/**
|
||||
* The protocol provider that created us.
|
||||
*/
|
||||
private ProtocolProviderServiceFacebookImpl parentProvider = null;
|
||||
|
||||
/**
|
||||
* Creates an instance of this operation set keeping a reference to the
|
||||
* parent protocol provider and presence operation set.
|
||||
*
|
||||
* @param provider The provider instance that creates us.
|
||||
* @param opSetPersPresence the currently valid
|
||||
* <tt>OperationSetPersistentPresenceFacebookImpl</tt>
|
||||
* instance.
|
||||
*/
|
||||
public OperationSetBasicInstantMessagingFacebookImpl(
|
||||
ProtocolProviderServiceFacebookImpl provider,
|
||||
OperationSetPersistentPresenceFacebookImpl opSetPersPresence)
|
||||
{
|
||||
this.opSetPersPresence = opSetPersPresence;
|
||||
this.parentProvider = provider;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements
|
||||
* AbstractOperationSetBasicInstantMessaging#createMessage(String, String,
|
||||
* String, String). Creates a new MessageFacebookImpl instance with the
|
||||
* specified properties.
|
||||
*/
|
||||
public Message createMessage(
|
||||
String content, String contentType, String encoding, String subject)
|
||||
{
|
||||
return
|
||||
new MessageFacebookImpl(content, contentType, encoding, subject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the <tt>message</tt> to the destination indicated by the
|
||||
* <tt>to</tt> contact.
|
||||
*
|
||||
* @param to the <tt>Contact</tt> to send <tt>message</tt> to
|
||||
* @param message the <tt>Message</tt> to send.
|
||||
* @throws IllegalStateException if the underlying ICQ stack is not
|
||||
* registered and initialized.
|
||||
* @throws IllegalArgumentException if <tt>to</tt> is not an instance
|
||||
* belonging to the underlying implementation.
|
||||
*/
|
||||
public void sendInstantMessage(final Contact to, final Message message)
|
||||
throws IllegalStateException,
|
||||
IllegalArgumentException
|
||||
{
|
||||
if (!(to instanceof ContactFacebookImpl))
|
||||
throw new IllegalArgumentException(
|
||||
"The specified contact is not a Facebook contact." + to);
|
||||
|
||||
Thread sender = new Thread(new Runnable()
|
||||
{
|
||||
|
||||
public void run()
|
||||
{
|
||||
// deliver the facebook chat Message
|
||||
MessageDeliveryFailedEvent errorEvent = null;
|
||||
errorEvent =
|
||||
OperationSetBasicInstantMessagingFacebookImpl.this.parentProvider
|
||||
.getAdapter().postFacebookChatMessage(message, to);
|
||||
if (errorEvent == null)
|
||||
{
|
||||
fireMessageDelivered(message, to);
|
||||
return;
|
||||
}
|
||||
if(errorEvent.getErrorCode() == FacebookErrorException.kError_Async_NotLoggedIn
|
||||
|| errorEvent.getErrorCode() == FacebookErrorException.kError_Async_LoginChanged)
|
||||
{
|
||||
try
|
||||
{
|
||||
parentProvider.unregister(RegistrationStateChangeEvent.REASON_MULTIPLE_LOGINS);
|
||||
}
|
||||
catch (OperationFailedException e1)
|
||||
{
|
||||
logger.error(
|
||||
"Unable to unregister the protocol provider: "
|
||||
+ this
|
||||
+ " due to the following exception: " + e1);
|
||||
}
|
||||
fireMessageDeliveryFailed(
|
||||
message,
|
||||
to,
|
||||
MessageDeliveryFailedEvent.PROVIDER_NOT_REGISTERED);
|
||||
return;
|
||||
}
|
||||
|
||||
// if the above delivery failed, we try again!
|
||||
try
|
||||
{
|
||||
// wait a moment
|
||||
Thread.sleep(1000);
|
||||
errorEvent =
|
||||
OperationSetBasicInstantMessagingFacebookImpl.this.parentProvider
|
||||
.getAdapter().postFacebookChatMessage(message, to);
|
||||
if (errorEvent == null)
|
||||
{
|
||||
fireMessageDelivered(message, to);
|
||||
return;
|
||||
}
|
||||
if(errorEvent.getErrorCode() == FacebookErrorException.kError_Async_NotLoggedIn
|
||||
|| errorEvent.getErrorCode() == FacebookErrorException.kError_Async_LoginChanged)
|
||||
{
|
||||
try
|
||||
{
|
||||
parentProvider.unregister(RegistrationStateChangeEvent.REASON_MULTIPLE_LOGINS);
|
||||
}
|
||||
catch (OperationFailedException e1)
|
||||
{
|
||||
logger.error(
|
||||
"Unable to unregister the protocol provider: "
|
||||
+ this
|
||||
+ " due to the following exception: " + e1);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
logger.warn(e.getMessage());
|
||||
}
|
||||
// if we get here, we have failed to deliver this message,
|
||||
// twice.
|
||||
fireMessageDeliveryFailed(
|
||||
message,
|
||||
to,
|
||||
MessageDeliveryFailedEvent.UNKNOWN_ERROR);
|
||||
// now do message delivery.----what's this?
|
||||
// in case that the "to" we deliver this message to is
|
||||
// ourselves?
|
||||
// deliverMessageToMyself(message, (ContactFacebookImpl)to);
|
||||
}
|
||||
});
|
||||
sender.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by the facebook adapter when we got messages from the server.
|
||||
*
|
||||
* @param message
|
||||
* @param from
|
||||
*/
|
||||
public void receivedInstantMessage(FacebookMessage fbmsg)
|
||||
{
|
||||
Message message = this.createMessage(fbmsg.getText());
|
||||
String fromID = fbmsg.getFrom();
|
||||
|
||||
// TODO handle the msgID.
|
||||
// it's generated when we createMessage() by now.
|
||||
// We should set the message id according to the fbmsg.msgID.
|
||||
// But it's not important.
|
||||
|
||||
Contact fromContact = opSetPersPresence.findContactByID(fromID);
|
||||
if (fromContact == null)
|
||||
{
|
||||
// from facebook user who are not on our contact list
|
||||
// TODO creat volatile contact, fire event.
|
||||
fromContact = opSetPersPresence.createVolatileContact(fromID);
|
||||
// opSetPersPresence.subscribe(fromID);
|
||||
}
|
||||
fireMessageReceived(message, fromContact);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all registered message listeners that a message has been
|
||||
* delivered successfully to its addressee..
|
||||
*
|
||||
* @param message
|
||||
* the <tt>Message</tt> that has been delivered.
|
||||
* @param to
|
||||
* the <tt>Contact</tt> that <tt>message</tt> was delivered to.
|
||||
*/
|
||||
protected void fireMessageDelivered(Message message, Contact to)
|
||||
{
|
||||
// we succeeded in sending a message to contact "to",
|
||||
// so we know he is online
|
||||
// but -- if we support the "invisible" status,
|
||||
// this line could be commented
|
||||
opSetPersPresence
|
||||
.setPresenceStatusForContact(
|
||||
(ContactFacebookImpl) to,
|
||||
FacebookStatusEnum.ONLINE);
|
||||
|
||||
super.fireMessageDelivered(message, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all registered message listeners that a message has been
|
||||
* received.
|
||||
*
|
||||
* @param message the <tt>Message</tt> that has been received.
|
||||
* @param from the <tt>Contact</tt> that <tt>message</tt> was received
|
||||
* from.
|
||||
*/
|
||||
protected void fireMessageReceived(Message message, Contact from)
|
||||
{
|
||||
// we got a message from contact "from", so we know he is online
|
||||
opSetPersPresence
|
||||
.setPresenceStatusForContact(
|
||||
(ContactFacebookImpl) from,
|
||||
FacebookStatusEnum.ONLINE);
|
||||
|
||||
super.fireMessageReceived(message, from);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the protocol provider (or the protocol itself) support
|
||||
* sending and receiving offline messages. Most often this method would
|
||||
* return true for protocols that support offline messages and false for
|
||||
* those that don't. It is however possible for a protocol to support these
|
||||
* messages and yet have a particular account that does not (i.e. feature
|
||||
* not enabled on the protocol server). In cases like this it is possible
|
||||
* for this method to return true even when offline messaging is not
|
||||
* supported, and then have the sendMessage method throw an
|
||||
* OperationFailedException with code - OFFLINE_MESSAGES_NOT_SUPPORTED.
|
||||
*
|
||||
* @return <tt>true</tt> if the protocol supports offline messages and
|
||||
* <tt>false</tt> otherwise.
|
||||
*/
|
||||
public boolean isOfflineMessagingSupported()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines wheter the protocol supports the supplied content type
|
||||
*
|
||||
* @param contentType the type we want to check
|
||||
* @return <tt>true</tt> if the protocol supports it and <tt>false</tt>
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean isContentTypeSupported(String contentType)
|
||||
{
|
||||
return contentType.equals(DEFAULT_MIME_TYPE);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.impl.protocol.facebook;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.service.protocol.event.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
/**
|
||||
* Instant messaging functionality for the Facebook protocol.
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
* @author Lubomir Marinov
|
||||
*/
|
||||
public class OperationSetBasicInstantMessagingFacebookImpl
|
||||
extends AbstractOperationSetBasicInstantMessaging
|
||||
{
|
||||
private static final Logger logger
|
||||
= Logger.getLogger(OperationSetBasicInstantMessagingFacebookImpl.class);
|
||||
|
||||
/**
|
||||
* The currently valid persistent presence operation set..
|
||||
*/
|
||||
private OperationSetPersistentPresenceFacebookImpl opSetPersPresence = null;
|
||||
|
||||
/**
|
||||
* The protocol provider that created us.
|
||||
*/
|
||||
private ProtocolProviderServiceFacebookImpl parentProvider = null;
|
||||
|
||||
/**
|
||||
* Creates an instance of this operation set keeping a reference to the
|
||||
* parent protocol provider and presence operation set.
|
||||
*
|
||||
* @param provider The provider instance that creates us.
|
||||
* @param opSetPersPresence the currently valid
|
||||
* <tt>OperationSetPersistentPresenceFacebookImpl</tt>
|
||||
* instance.
|
||||
*/
|
||||
public OperationSetBasicInstantMessagingFacebookImpl(
|
||||
ProtocolProviderServiceFacebookImpl provider,
|
||||
OperationSetPersistentPresenceFacebookImpl opSetPersPresence)
|
||||
{
|
||||
this.opSetPersPresence = opSetPersPresence;
|
||||
this.parentProvider = provider;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements
|
||||
* AbstractOperationSetBasicInstantMessaging#createMessage(String, String,
|
||||
* String, String). Creates a new MessageFacebookImpl instance with the
|
||||
* specified properties.
|
||||
*/
|
||||
public Message createMessage(
|
||||
String content, String contentType, String encoding, String subject)
|
||||
{
|
||||
return
|
||||
new MessageFacebookImpl(content, contentType, encoding, subject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the <tt>message</tt> to the destination indicated by the
|
||||
* <tt>to</tt> contact.
|
||||
*
|
||||
* @param to the <tt>Contact</tt> to send <tt>message</tt> to
|
||||
* @param message the <tt>Message</tt> to send.
|
||||
* @throws IllegalStateException if the underlying ICQ stack is not
|
||||
* registered and initialized.
|
||||
* @throws IllegalArgumentException if <tt>to</tt> is not an instance
|
||||
* belonging to the underlying implementation.
|
||||
*/
|
||||
public void sendInstantMessage(final Contact to, final Message message)
|
||||
throws IllegalStateException,
|
||||
IllegalArgumentException
|
||||
{
|
||||
if (!(to instanceof ContactFacebookImpl))
|
||||
throw new IllegalArgumentException(
|
||||
"The specified contact is not a Facebook contact." + to);
|
||||
|
||||
Thread sender = new Thread(new Runnable()
|
||||
{
|
||||
|
||||
public void run()
|
||||
{
|
||||
// deliver the facebook chat Message
|
||||
MessageDeliveryFailedEvent errorEvent = null;
|
||||
errorEvent =
|
||||
OperationSetBasicInstantMessagingFacebookImpl.this.parentProvider
|
||||
.getAdapter().postFacebookChatMessage(message, to);
|
||||
if (errorEvent == null)
|
||||
{
|
||||
fireMessageDelivered(message, to);
|
||||
return;
|
||||
}
|
||||
if(errorEvent.getErrorCode() == FacebookErrorException.kError_Async_NotLoggedIn
|
||||
|| errorEvent.getErrorCode() == FacebookErrorException.kError_Async_LoginChanged)
|
||||
{
|
||||
try
|
||||
{
|
||||
parentProvider.unregister(RegistrationStateChangeEvent.REASON_MULTIPLE_LOGINS);
|
||||
}
|
||||
catch (OperationFailedException e1)
|
||||
{
|
||||
logger.error(
|
||||
"Unable to unregister the protocol provider: "
|
||||
+ this
|
||||
+ " due to the following exception: " + e1);
|
||||
}
|
||||
fireMessageDeliveryFailed(
|
||||
message,
|
||||
to,
|
||||
MessageDeliveryFailedEvent.PROVIDER_NOT_REGISTERED);
|
||||
return;
|
||||
}
|
||||
|
||||
// if the above delivery failed, we try again!
|
||||
try
|
||||
{
|
||||
// wait a moment
|
||||
Thread.sleep(1000);
|
||||
errorEvent =
|
||||
OperationSetBasicInstantMessagingFacebookImpl.this.parentProvider
|
||||
.getAdapter().postFacebookChatMessage(message, to);
|
||||
if (errorEvent == null)
|
||||
{
|
||||
fireMessageDelivered(message, to);
|
||||
return;
|
||||
}
|
||||
if(errorEvent.getErrorCode() == FacebookErrorException.kError_Async_NotLoggedIn
|
||||
|| errorEvent.getErrorCode() == FacebookErrorException.kError_Async_LoginChanged)
|
||||
{
|
||||
try
|
||||
{
|
||||
parentProvider.unregister(RegistrationStateChangeEvent.REASON_MULTIPLE_LOGINS);
|
||||
}
|
||||
catch (OperationFailedException e1)
|
||||
{
|
||||
logger.error(
|
||||
"Unable to unregister the protocol provider: "
|
||||
+ this
|
||||
+ " due to the following exception: " + e1);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
logger.warn(e.getMessage());
|
||||
}
|
||||
// if we get here, we have failed to deliver this message,
|
||||
// twice.
|
||||
fireMessageDeliveryFailed(
|
||||
message,
|
||||
to,
|
||||
MessageDeliveryFailedEvent.UNKNOWN_ERROR);
|
||||
// now do message delivery.----what's this?
|
||||
// in case that the "to" we deliver this message to is
|
||||
// ourselves?
|
||||
// deliverMessageToMyself(message, (ContactFacebookImpl)to);
|
||||
}
|
||||
});
|
||||
sender.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by the facebook adapter when we got messages from the server.
|
||||
*
|
||||
* @param fbmsg the received Facebook instant message
|
||||
*/
|
||||
public void receivedInstantMessage(FacebookMessage fbmsg)
|
||||
{
|
||||
Message message = this.createMessage(fbmsg.getText());
|
||||
String fromID = fbmsg.getFrom();
|
||||
|
||||
// TODO handle the msgID.
|
||||
// it's generated when we createMessage() by now.
|
||||
// We should set the message id according to the fbmsg.msgID.
|
||||
// But it's not important.
|
||||
|
||||
Contact fromContact = opSetPersPresence.findContactByID(fromID);
|
||||
if (fromContact == null)
|
||||
{
|
||||
// from facebook user who are not on our contact list
|
||||
// TODO creat volatile contact, fire event.
|
||||
fromContact = opSetPersPresence.createVolatileContact(fromID);
|
||||
// opSetPersPresence.subscribe(fromID);
|
||||
}
|
||||
fireMessageReceived(message, fromContact);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all registered message listeners that a message has been
|
||||
* delivered successfully to its addressee..
|
||||
*
|
||||
* @param message
|
||||
* the <tt>Message</tt> that has been delivered.
|
||||
* @param to
|
||||
* the <tt>Contact</tt> that <tt>message</tt> was delivered to.
|
||||
*/
|
||||
protected void fireMessageDelivered(Message message, Contact to)
|
||||
{
|
||||
// we succeeded in sending a message to contact "to",
|
||||
// so we know he is online
|
||||
// but -- if we support the "invisible" status,
|
||||
// this line could be commented
|
||||
opSetPersPresence
|
||||
.setPresenceStatusForContact(
|
||||
(ContactFacebookImpl) to,
|
||||
FacebookStatusEnum.ONLINE);
|
||||
|
||||
super.fireMessageDelivered(message, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all registered message listeners that a message has been
|
||||
* received.
|
||||
*
|
||||
* @param message the <tt>Message</tt> that has been received.
|
||||
* @param from the <tt>Contact</tt> that <tt>message</tt> was received
|
||||
* from.
|
||||
*/
|
||||
protected void fireMessageReceived(Message message, Contact from)
|
||||
{
|
||||
// we got a message from contact "from", so we know he is online
|
||||
opSetPersPresence
|
||||
.setPresenceStatusForContact(
|
||||
(ContactFacebookImpl) from,
|
||||
FacebookStatusEnum.ONLINE);
|
||||
|
||||
super.fireMessageReceived(message, from);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the protocol provider (or the protocol itself) support
|
||||
* sending and receiving offline messages. Most often this method would
|
||||
* return true for protocols that support offline messages and false for
|
||||
* those that don't. It is however possible for a protocol to support these
|
||||
* messages and yet have a particular account that does not (i.e. feature
|
||||
* not enabled on the protocol server). In cases like this it is possible
|
||||
* for this method to return true even when offline messaging is not
|
||||
* supported, and then have the sendMessage method throw an
|
||||
* OperationFailedException with code - OFFLINE_MESSAGES_NOT_SUPPORTED.
|
||||
*
|
||||
* @return <tt>true</tt> if the protocol supports offline messages and
|
||||
* <tt>false</tt> otherwise.
|
||||
*/
|
||||
public boolean isOfflineMessagingSupported()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines wheter the protocol supports the supplied content type
|
||||
*
|
||||
* @param contentType the type we want to check
|
||||
* @return <tt>true</tt> if the protocol supports it and <tt>false</tt>
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean isContentTypeSupported(String contentType)
|
||||
{
|
||||
return contentType.equals(DEFAULT_MIME_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,194 +1,192 @@
|
||||
/*
|
||||
* 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.autoaway;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.service.configuration.*;
|
||||
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 final Map<ProtocolProviderService, PresenceStatus> lastStates
|
||||
= new HashMap<ProtocolProviderService, PresenceStatus>();
|
||||
|
||||
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
|
||||
{
|
||||
if (MouseInfo.getPointerInfo() != null)
|
||||
{
|
||||
PointerInfo info = MouseInfo.getPointerInfo();
|
||||
Point currentPosition
|
||||
= (info != null) ? info.getLocation() : new Point(0, 0);
|
||||
|
||||
if (!isNear(lastPosition, currentPosition))
|
||||
{
|
||||
// position has changed
|
||||
// check, if a minor state has been automatically set
|
||||
// and
|
||||
// reset this state to the former state.
|
||||
for (ProtocolProviderService protocolProviderService
|
||||
: AutoAwayActivator.getProtocolProviders())
|
||||
{
|
||||
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
|
||||
for (ProtocolProviderService protocolProviderService
|
||||
: AutoAwayActivator.getProtocolProviders())
|
||||
{
|
||||
OperationSetPresence presence
|
||||
= (OperationSetPresence)
|
||||
protocolProviderService
|
||||
.getOperationSet(
|
||||
OperationSetPresence.class);
|
||||
|
||||
PresenceStatus status = presence
|
||||
.getPresenceStatus();
|
||||
|
||||
if (status.getStatus()
|
||||
< PresenceStatus.AVAILABLE_THRESHOLD)
|
||||
{
|
||||
// already (manually) set to away or lower
|
||||
continue;
|
||||
}
|
||||
|
||||
lastStates.put(protocolProviderService, status);
|
||||
|
||||
PresenceStatus newStatus = findAwayStatus(presence);
|
||||
|
||||
try
|
||||
{
|
||||
if (newStatus != null)
|
||||
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<PresenceStatus> statusSet = presence.getSupportedStatusSet();
|
||||
PresenceStatus status = null;
|
||||
|
||||
while (statusSet.hasNext())
|
||||
{
|
||||
PresenceStatus possibleState = statusSet.next();
|
||||
int possibleStatus = possibleState.getStatus();
|
||||
|
||||
if ((possibleStatus < PresenceStatus.AVAILABLE_THRESHOLD)
|
||||
&& (possibleStatus >= PresenceStatus.ONLINE_THRESHOLD))
|
||||
{
|
||||
if (status == null
|
||||
|| (Math.abs(possibleStatus - AWAY_DEFAULT_STATUS)
|
||||
< Math.abs(
|
||||
status.getStatus()
|
||||
- AWAY_DEFAULT_STATUS)))
|
||||
{
|
||||
status = possibleState;
|
||||
}
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
private int getTimer()
|
||||
{
|
||||
ConfigurationService configService
|
||||
= AutoAwayActivator.getConfigService();
|
||||
|
||||
return
|
||||
configService.getBoolean(Preferences.ENABLE, false)
|
||||
? configService.getInt(Preferences.TIMER, 0)
|
||||
: 0;
|
||||
}
|
||||
|
||||
public boolean isRunning()
|
||||
{
|
||||
return run;
|
||||
}
|
||||
|
||||
private boolean isNear(Point p1, Point p2)
|
||||
{
|
||||
return
|
||||
(p1 != null)
|
||||
&& (p2 != null)
|
||||
&& (Math.abs(p1.x - p2.x) <= 10)
|
||||
&& (Math.abs(p1.y - p2.y) <= 10);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.autoaway;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.service.configuration.*;
|
||||
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 final Map<ProtocolProviderService, PresenceStatus> lastStates
|
||||
= new HashMap<ProtocolProviderService, PresenceStatus>();
|
||||
|
||||
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
|
||||
{
|
||||
if (MouseInfo.getPointerInfo() != null)
|
||||
{
|
||||
PointerInfo info = MouseInfo.getPointerInfo();
|
||||
Point currentPosition
|
||||
= (info != null) ? info.getLocation() : new Point(0, 0);
|
||||
|
||||
if (!isNear(lastPosition, currentPosition))
|
||||
{
|
||||
// position has changed
|
||||
// check, if a minor state has been automatically set
|
||||
// and
|
||||
// reset this state to the former state.
|
||||
for (ProtocolProviderService protocolProviderService
|
||||
: AutoAwayActivator.getProtocolProviders())
|
||||
{
|
||||
if (lastStates.get(protocolProviderService) != null)
|
||||
{
|
||||
PresenceStatus lastState
|
||||
= lastStates.get(protocolProviderService);
|
||||
OperationSetPresence presence
|
||||
= 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
|
||||
for (ProtocolProviderService protocolProviderService
|
||||
: AutoAwayActivator.getProtocolProviders())
|
||||
{
|
||||
OperationSetPresence presence
|
||||
= protocolProviderService
|
||||
.getOperationSet(
|
||||
OperationSetPresence.class);
|
||||
|
||||
PresenceStatus status = presence
|
||||
.getPresenceStatus();
|
||||
|
||||
if (status.getStatus()
|
||||
< PresenceStatus.AVAILABLE_THRESHOLD)
|
||||
{
|
||||
// already (manually) set to away or lower
|
||||
continue;
|
||||
}
|
||||
|
||||
lastStates.put(protocolProviderService, status);
|
||||
|
||||
PresenceStatus newStatus = findAwayStatus(presence);
|
||||
|
||||
try
|
||||
{
|
||||
if (newStatus != null)
|
||||
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<PresenceStatus> statusSet = presence.getSupportedStatusSet();
|
||||
PresenceStatus status = null;
|
||||
|
||||
while (statusSet.hasNext())
|
||||
{
|
||||
PresenceStatus possibleState = statusSet.next();
|
||||
int possibleStatus = possibleState.getStatus();
|
||||
|
||||
if ((possibleStatus < PresenceStatus.AVAILABLE_THRESHOLD)
|
||||
&& (possibleStatus >= PresenceStatus.ONLINE_THRESHOLD))
|
||||
{
|
||||
if (status == null
|
||||
|| (Math.abs(possibleStatus - AWAY_DEFAULT_STATUS)
|
||||
< Math.abs(
|
||||
status.getStatus()
|
||||
- AWAY_DEFAULT_STATUS)))
|
||||
{
|
||||
status = possibleState;
|
||||
}
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
private int getTimer()
|
||||
{
|
||||
ConfigurationService configService
|
||||
= AutoAwayActivator.getConfigService();
|
||||
|
||||
return
|
||||
configService.getBoolean(Preferences.ENABLE, false)
|
||||
? configService.getInt(Preferences.TIMER, 0)
|
||||
: 0;
|
||||
}
|
||||
|
||||
public boolean isRunning()
|
||||
{
|
||||
return run;
|
||||
}
|
||||
|
||||
private boolean isNear(Point p1, Point p2)
|
||||
{
|
||||
return
|
||||
(p1 != null)
|
||||
&& (p2 != null)
|
||||
&& (Math.abs(p1.x - p2.x) <= 10)
|
||||
&& (Math.abs(p1.y - p2.y) <= 10);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue