merge beem-audio with trunk + some bugfix/improvements
authorNikita Kozlov <nikita@mbdsys.com>
Sun, 26 Dec 2010 01:01:58 +0100
changeset 840 322071d17c00
parent 839 0e5b95573614
child 841 dfee4a6ffdad
merge beem-audio with trunk + some bugfix/improvements
doc/asmack-beem/beem-build-process.patch
doc/asmack-beem/beem_patches/50-jingle-ext.patch
doc/asmack-beem/beem_patches/50-remove-jingle_mediaimpl.patch
doc/asmack-beem/lib/jstun.jar
libs/asmack-android-7-beem.jar
res/menu/contactlist_context.xml
src/com/beem/project/beem/jingle/JingleService.java
src/com/beem/project/beem/jingle/MicrophoneRTPManager.java
src/com/beem/project/beem/service/XmppFacade.java
src/com/beem/project/beem/ui/Call.java
src/com/beem/project/beem/ui/ContactList.java
--- a/doc/asmack-beem/beem-build-process.patch	Sat Dec 25 17:01:00 2010 +0100
+++ b/doc/asmack-beem/beem-build-process.patch	Sun Dec 26 01:01:58 2010 +0100
@@ -1,6 +1,8 @@
---- asmack-git/build.bash	2010-05-07 22:08:18.000000000 +0200
-+++ asmack/build.bash	2010-05-07 22:06:53.000000000 +0200
-@@ -15,11 +15,11 @@
+diff --git a/build.bash b/build.bash
+index ce793f9..26e4560 100755
+--- a/build.bash
++++ b/build.bash
+@@ -15,11 +15,11 @@ fetch() {
    if ! [ -f "${2}/.svn/entries" ]; then
      mkdir "${2}"
      cd "${2}"
@@ -14,7 +16,7 @@
    fi
  )
  }
-@@ -37,11 +37,11 @@
+@@ -37,11 +37,11 @@ gitfetch() {
  }
  
  fetchall() {
@@ -31,3 +33,51 @@
  }
  
  copyfolder() {
+@@ -62,6 +62,7 @@ buildsrc() {
+   mkdir build/src
+   mkdir build/src/trunk
+   copyfolder "src/smack/source/" "build/src/trunk" "."
++  copyfolder "src/smack/jingle/extension/source/" "build/src/trunk" "."
+   copyfolder "src/qpid/java" "build/src/trunk" "org/apache/qpid/management/common/sasl"
+   copyfolder "src/novell-openldap-jldap" "build/src/trunk" "."
+   copyfolder "src/dnsjava"  "build/src/trunk" "org"
+diff --git a/build.xml b/build.xml
+index 827f4c5..50054f1 100644
+--- a/build.xml
++++ b/build.xml
+@@ -57,7 +57,7 @@
+       target="1.5"
+       srcdir="build/src/trunk"
+       destdir="build/classes/trunk"
+-      classpath="lib/xmlpull_1_1_3_4c.jar"
++      classpath="lib/xmlpull_1_1_3_4c.jar:lib/jstun.jar"
+       bootclasspath="${sdk-location}/platforms/android-7/android.jar"
+       debug="true"
+       debuglevel="source,lines"
+@@ -85,7 +85,7 @@
+       target="1.5"
+       srcdir="build/src/trunk"
+       destdir="build/classes/trunk"
+-      classpath="lib/xmlpull_1_1_3_4c.jar"
++      classpath="lib/xmlpull_1_1_3_4c.jar:lib/jstun.jar"
+       bootclasspath="${sdk-location}/platforms/android-6/android.jar"
+       debug="true"
+       debuglevel="source,lines"
+@@ -113,7 +113,7 @@
+       target="1.5"
+       srcdir="build/src/trunk"
+       destdir="build/classes/trunk"
+-      classpath="lib/xmlpull_1_1_3_4c.jar"
++      classpath="lib/xmlpull_1_1_3_4c.jar:lib/jstun.jar"
+       bootclasspath="${sdk-location}/platforms/android-5/android.jar"
+       debug="true"
+       debuglevel="source,lines"
+@@ -141,7 +141,7 @@
+       target="1.5"
+       srcdir="build/src/trunk"
+       destdir="build/classes/trunk"
+-      classpath="lib/xmlpull_1_1_3_4c.jar"
++      classpath="lib/xmlpull_1_1_3_4c.jar:lib/jstun.jar"
+       bootclasspath="${sdk-location}/platforms/android-4/android.jar"
+       debug="true"
+       debuglevel="source,lines"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/asmack-beem/beem_patches/50-jingle-ext.patch	Sun Dec 26 01:01:58 2010 +0100
@@ -0,0 +1,98 @@
+Index: org/jivesoftware/smackx/packet/JingleContentInfo.java
+===================================================================
+--- org/jivesoftware/smackx/packet/JingleContentInfo.java	(revision 11644)
++++ org/jivesoftware/smackx/packet/JingleContentInfo.java	(working copy)
+@@ -96,7 +96,7 @@
+      */
+     public static class Audio extends JingleContentInfo {
+ 
+-        public static final String NAMESPACE = "urn:xmpp:tmp:jingle:apps:rtp";
++        public static final String NAMESPACE = "urn:xmpp:jingle:apps:rtp:1";
+ 
+         public Audio(final ContentInfo mi) {
+             super(mi);
+Index: org/jivesoftware/smackx/packet/JingleError.java
+===================================================================
+--- org/jivesoftware/smackx/packet/JingleError.java	(revision 11644)
++++ org/jivesoftware/smackx/packet/JingleError.java	(working copy)
+@@ -27,7 +27,7 @@
+ 
+ public class JingleError implements PacketExtension {
+ 
+-    public static String NAMESPACE = "urn:xmpp:tmp:jingle:errors";
++    public static String NAMESPACE = "urn:xmpp:jingle:errors:1";
+ 
+     public static final JingleError OUT_OF_ORDER = new JingleError("out-of-order");
+ 
+Index: org/jivesoftware/smackx/packet/JingleTransport.java
+===================================================================
+--- org/jivesoftware/smackx/packet/JingleTransport.java	(revision 11644)
++++ org/jivesoftware/smackx/packet/JingleTransport.java	(working copy)
+@@ -270,7 +270,7 @@
+      * RTP-ICE profile
+      */
+     public static class Ice extends JingleTransport {
+-        public static final String NAMESPACE = "urn:xmpp:tmp:jingle:transports:ice-udp";
++        public static final String NAMESPACE = "urn:xmpp:jingle:transports:ice-udp:1";
+ 
+         public Ice() {
+             super();
+Index: org/jivesoftware/smackx/packet/JingleContentDescription.java
+===================================================================
+--- org/jivesoftware/smackx/packet/JingleContentDescription.java	(revision 11644)
++++ org/jivesoftware/smackx/packet/JingleContentDescription.java	(working copy)
+@@ -172,7 +172,7 @@
+      */
+     public static class Audio extends JingleContentDescription {
+ 
+-        public static final String NAMESPACE = "urn:xmpp:tmp:jingle:apps:rtp";
++        public static final String NAMESPACE = "urn:xmpp:jingle:apps:rtp:1";
+ 
+         public Audio() {
+             super();
+Index: org/jivesoftware/smackx/packet/Jingle.java
+===================================================================
+--- org/jivesoftware/smackx/packet/Jingle.java	(revision 11644)
++++ org/jivesoftware/smackx/packet/Jingle.java	(working copy)
+@@ -44,7 +44,7 @@
+ 
+     // static
+ 
+-    public static final String NAMESPACE = "urn:xmpp:tmp:jingle";
++    public static final String NAMESPACE = "urn:xmpp:jingle:1";
+ 
+     public static final String NODENAME = "jingle";
+ 
+Index: org/jivesoftware/smackx/jingle/JingleManager.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/JingleManager.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/JingleManager.java	(working copy)
+@@ -255,7 +255,7 @@
+      */
+     public static void setJingleServiceEnabled() {
+         ProviderManager providerManager = ProviderManager.getInstance();
+-        providerManager.addIQProvider("jingle", "urn:xmpp:tmp:jingle", new JingleProvider());
++        providerManager.addIQProvider("jingle", "urn:xmpp:jingle:1", new JingleProvider());
+ 
+         // Enable the Jingle support on every established connection
+         // The ServiceDiscoveryManager class should have been already
+@@ -598,4 +598,4 @@
+         }
+         return null;
+     }
+-}
+\ No newline at end of file
++}
+Index: org/jivesoftware/smackx/packet/JingleDescription.java
+===================================================================
+--- org/jivesoftware/smackx/packet/JingleDescription.java	(revision 11644)
++++ org/jivesoftware/smackx/packet/JingleDescription.java	(working copy)
+@@ -179,7 +179,7 @@
+      */
+     public static class Audio extends JingleDescription {
+ 
+-        public static final String NAMESPACE = "urn:xmpp:tmp:jingle:apps:rtp";
++        public static final String NAMESPACE = "urn:xmpp:jingle:apps:rtp:1";
+ 
+         public Audio() {
+             super();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/asmack-beem/beem_patches/50-remove-jingle_mediaimpl.patch	Sun Dec 26 01:01:58 2010 +0100
@@ -0,0 +1,4039 @@
+Index: org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaSession.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaSession.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaSession.java	(working copy)
+@@ -1,92 +0,0 @@
+-/**
+- * $RCSfile: TestMediaSession.java,v $
+- * $Revision: 1.1 $
+- * $Date: 08/11/2006
+- * <p/>
+- * Copyright 2003-2006 Jive Software.
+- * <p/>
+- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- * <p/>
+- * http://www.apache.org/licenses/LICENSE-2.0
+- * <p/>
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.jivesoftware.smackx.jingle.mediaimpl.test;
+-
+-import org.jivesoftware.smackx.jingle.JingleSession;
+-import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
+-import org.jivesoftware.smackx.jingle.media.PayloadType;
+-import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
+-
+-/**
+- * This Class implements a complete JingleMediaSession for unit testing.
+- *
+- * @author Thiago Camargo
+- */
+-public class TestMediaSession extends JingleMediaSession {
+-
+-     /**
+-     * Creates a TestMediaSession with defined payload type, remote and local candidates
+-     *
+-     * @param payloadType Payload of the jmf
+-     * @param remote      the remote information. The candidate that the jmf will be sent to.
+-     * @param local       the local information. The candidate that will receive the jmf
+-     * @param locator     media locator
+-     */
+-    public TestMediaSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local,
+-            final String locator, JingleSession jingleSession) {
+-        super(payloadType, remote, local, "Test", jingleSession);
+-        initialize();
+-    }
+-
+-    /**
+-     * Initialize the screen share channels.
+-     */
+-    public void initialize() {
+-
+-    }
+-
+-    /**
+-     * Starts transmission and for NAT Traversal reasons start receiving also.
+-     */
+-    public void startTrasmit() {
+-        
+-    }
+-
+-    /**
+-     * Set transmit activity. If the active is true, the instance should trasmit.
+-     * If it is set to false, the instance should pause transmit.
+-     *
+-     * @param active active state
+-     */
+-    public void setTrasmit(boolean active) {
+-        
+-    }
+-
+-    /**
+-     * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
+-     */
+-    public void startReceive() {
+-        // Do nothing
+-    }
+-
+-    /**
+-     * Stops transmission and for NAT Traversal reasons stop receiving also.
+-     */
+-    public void stopTrasmit() {
+-       
+-    }
+-
+-    /**
+-     * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
+-     */
+-    public void stopReceive() {
+-       
+-    }
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaManager.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaManager.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaManager.java	(working copy)
+@@ -1,93 +0,0 @@
+-/**
+- * $RCSfile: TestMediaManager.java,v $
+- * $Revision: 1.3 $
+- * $Date: 25/12/2006
+- * <p/>
+- * Copyright 2003-2006 Jive Software.
+- * <p/>
+- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- * <p/>
+- * http://www.apache.org/licenses/LICENSE-2.0
+- * <p/>
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.jivesoftware.smackx.jingle.mediaimpl.test;
+-
+-import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
+-import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
+-import org.jivesoftware.smackx.jingle.media.PayloadType;
+-import org.jivesoftware.smackx.jingle.nat.JingleTransportManager;
+-import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
+-import org.jivesoftware.smackx.jingle.JingleSession;
+-
+-import java.util.*;
+-
+-/**
+- * Implements a MediaManager for test purposes.
+- *
+- * @author Thiago Camargo
+- */
+-
+-public class TestMediaManager extends JingleMediaManager {
+-
+-    public static final String MEDIA_NAME = "TestMedia";
+-
+-    private List<PayloadType> payloads = new ArrayList<PayloadType>();
+-
+-    private PayloadType preferredPayloadType = null;
+-
+-    public TestMediaManager(JingleTransportManager transportManager) {
+-        super(transportManager);
+-    }
+-
+-    /**
+-    * Return all supported Payloads for this Manager.
+-    *
+-    * @return The Payload List
+-    */
+-    public List<PayloadType> getPayloads() {
+-        return payloads;
+-    }
+-
+-    public void setPayloads(List<PayloadType> payloads) {
+-        this.payloads.addAll(payloads);
+-    }
+-
+-    /**
+-     * Returns a new JingleMediaSession
+-     *
+-     * @param payloadType payloadType
+-     * @param remote      remote Candidate
+-     * @param local       local Candidate
+-     * @return JingleMediaSession JingleMediaSession
+-     */
+-    public JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote,
+-            final TransportCandidate local, final JingleSession jingleSession) {
+-        TestMediaSession session = null;
+-
+-        session = new TestMediaSession(payloadType, remote, local, "", jingleSession);
+-
+-        return session;
+-    }
+-
+-    public PayloadType getPreferredPayloadType() {
+-        if (preferredPayloadType != null)
+-            return preferredPayloadType;
+-        return super.getPreferredPayloadType();
+-    }
+-
+-    public void setPreferredPayloadType(PayloadType preferredPayloadType) {
+-        this.preferredPayloadType = preferredPayloadType;
+-    }
+-
+-    public String getName() {
+-        return MEDIA_NAME;
+-    }
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/JMFInit.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/JMFInit.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/JMFInit.java	(working copy)
+@@ -1,282 +0,0 @@
+-package org.jivesoftware.smackx.jingle.mediaimpl;
+-
+-import java.awt.Frame;
+-import java.awt.TextArea;
+-import java.awt.Toolkit;
+-import java.util.Vector;
+-
+-import javax.media.Format;
+-import javax.media.PlugInManager;
+-import javax.media.Renderer;
+-import javax.media.format.AudioFormat;
+-
+-import org.jivesoftware.smackx.jingle.SmackLogger;
+-
+-import com.sun.media.ExclusiveUse;
+-import com.sun.media.util.Registry;
+-
+-public class JMFInit extends Frame implements Runnable {
+-
+-	private static final SmackLogger LOGGER = SmackLogger.getLogger(JMFInit.class);
+-
+-	private String tempDir = "/tmp";
+-
+-    private boolean done = false;
+-
+-    private String userHome;
+-
+-    private boolean visible = false;
+-
+-    public JMFInit(String[] args, boolean visible) {
+-        super("Initializing JMF...");
+-
+-        this.visible = visible;
+-
+-        Registry.set("secure.allowCaptureFromApplets", true);
+-        Registry.set("secure.allowSaveFileFromApplets", true);
+-
+-        updateTemp(args);
+-
+-        try {
+-            Registry.commit();
+-        }
+-        catch (Exception e) {
+-
+-            message("Failed to commit to JMFRegistry!");
+-        }
+-
+-        Thread detectThread = new Thread(this);
+-        detectThread.run();
+-
+-        /*
+-           * int slept = 0; while (!done && slept < 60 * 1000 * 2) { try {
+-           * Thread.currentThread().sleep(500); } catch (InterruptedException ie) { }
+-           * slept += 500; }
+-           *
+-           * if (!done) { console.error("Detection is taking too long!
+-           * Aborting!"); message("Detection is taking too long! Aborting!"); }
+-           *
+-           * try { Thread.currentThread().sleep(2000); } catch
+-           * (InterruptedException ie) { }
+-           */
+-    }
+-
+-    public void run() {
+-        detectDirectAudio();
+-        detectS8DirectAudio();
+-        detectCaptureDevices();
+-        done = true;
+-    }
+-
+-    private void updateTemp(String[] args) {
+-        if (args != null && args.length > 0) {
+-            tempDir = args[0];
+-
+-            message("Setting cache directory to " + tempDir);
+-            Registry r = new Registry();
+-            try {
+-                r.set("secure.cacheDir", tempDir);
+-                r.commit();
+-
+-                message("Updated registry");
+-            }
+-            catch (Exception e) {
+-                message("Couldn't update registry!");
+-            }
+-        }
+-    }
+-
+-    private void detectCaptureDevices() {
+-        // check if JavaSound capture is available
+-        message("Looking for Audio capturer");
+-        Class dsauto;
+-        try {
+-            dsauto = Class.forName("DirectSoundAuto");
+-            dsauto.newInstance();
+-            message("Finished detecting DirectSound capturer");
+-        }
+-        catch (ThreadDeath td) {
+-            throw td;
+-        }
+-        catch (Throwable t) {
+-            //Do nothing
+-        }
+-
+-        Class jsauto;
+-        try {
+-            jsauto = Class.forName("JavaSoundAuto");
+-            jsauto.newInstance();
+-            message("Finished detecting javasound capturer");
+-        }
+-        catch (ThreadDeath td) {
+-            throw td;
+-        }
+-        catch (Throwable t) {
+-            message("JavaSound capturer detection failed!");
+-        }
+-
+-        /*
+-        // Check if VFWAuto or SunVideoAuto is available
+-        message("Looking for video capture devices");
+-        Class auto = null;
+-        Class autoPlus = null;
+-        try {
+-            auto = Class.forName("VFWAuto");
+-        }
+-        catch (Exception e) {
+-        }
+-        if (auto == null) {
+-            try {
+-                auto = Class.forName("SunVideoAuto");
+-            }
+-            catch (Exception ee) {
+-
+-            }
+-            try {
+-                autoPlus = Class.forName("SunVideoPlusAuto");
+-            }
+-            catch (Exception ee) {
+-
+-            }
+-        }
+-        if (auto == null) {
+-            try {
+-                auto = Class.forName("V4LAuto");
+-            }
+-            catch (Exception ee) {
+-
+-            }
+-        }
+-        try {
+-            Object instance = auto.newInstance();
+-            if (autoPlus != null) {
+-                Object instancePlus = autoPlus.newInstance();
+-            }
+-
+-            message("Finished detecting video capture devices");
+-        }
+-        catch (ThreadDeath td) {
+-            throw td;
+-        }
+-        catch (Throwable t) {
+-
+-            message("Capture device detection failed!");
+-        }
+-        */
+-    }
+-
+-    private void detectDirectAudio() {
+-        Class cls;
+-        int plType = PlugInManager.RENDERER;
+-        String dar = "com.sun.media.renderer.audio.DirectAudioRenderer";
+-        try {
+-            // Check if this is the Windows Performance Pack - hack
+-            cls = Class.forName("VFWAuto");
+-            // Check if DS capture is supported, otherwise fail DS renderer
+-            // since NT doesn't have capture
+-            cls = Class.forName("com.sun.media.protocol.dsound.DSound");
+-            // Find the renderer class and instantiate it.
+-            cls = Class.forName(dar);
+-
+-            Renderer rend = (Renderer) cls.newInstance();
+-            try {
+-                // Set the format and open the device
+-                AudioFormat af = new AudioFormat(AudioFormat.LINEAR, 44100, 16,
+-                        2);
+-                rend.setInputFormat(af);
+-                rend.open();
+-                Format[] inputFormats = rend.getSupportedInputFormats();
+-                // Register the device
+-                PlugInManager.addPlugIn(dar, inputFormats, new Format[0],
+-                        plType);
+-                // Move it to the top of the list
+-                Vector rendList = PlugInManager.getPlugInList(null, null,
+-                        plType);
+-                int listSize = rendList.size();
+-                if (rendList.elementAt(listSize - 1).equals(dar)) {
+-                    rendList.removeElementAt(listSize - 1);
+-                    rendList.insertElementAt(dar, 0);
+-                    PlugInManager.setPlugInList(rendList, plType);
+-                    PlugInManager.commit();
+-                    // Log.debug("registered");
+-                }
+-                rend.close();
+-            }
+-            catch (Throwable t) {
+-                // Log.debug("Error " + t);
+-            }
+-        }
+-        catch (Throwable tt) {
+-            //Do nothing
+-        }
+-    }
+-
+-    private void detectS8DirectAudio() {
+-        Class cls;
+-        int plType = PlugInManager.RENDERER;
+-        String dar = "com.sun.media.renderer.audio.DirectAudioRenderer";
+-        try {
+-            // Check if this is the solaris Performance Pack - hack
+-            cls = Class.forName("SunVideoAuto");
+-
+-            // Find the renderer class and instantiate it.
+-            cls = Class.forName(dar);
+-
+-            Renderer rend = (Renderer) cls.newInstance();
+-
+-            if (rend instanceof ExclusiveUse
+-                    && !((ExclusiveUse) rend).isExclusive()) {
+-                // sol8+, DAR supports mixing
+-                Vector rendList = PlugInManager.getPlugInList(null, null,
+-                        plType);
+-                int listSize = rendList.size();
+-                boolean found = false;
+-                String rname = null;
+-
+-                for (int i = 0; i < listSize; i++) {
+-                    rname = (String) (rendList.elementAt(i));
+-                    if (rname.equals(dar)) { // DAR is in the registry
+-                        found = true;
+-                        rendList.removeElementAt(i);
+-                        break;
+-                    }
+-                }
+-
+-                if (found) {
+-                    rendList.insertElementAt(dar, 0);
+-                    PlugInManager.setPlugInList(rendList, plType);
+-                    PlugInManager.commit();
+-                }
+-            }
+-        }
+-        catch (Throwable tt) {
+-            //Do nothing
+-        }
+-    }
+-
+-    private void message(String mesg) {
+-        LOGGER.debug(mesg);
+-    }
+-
+-    private void createGUI() {
+-        TextArea textBox = new TextArea(5, 50);
+-        add("Center", textBox);
+-        textBox.setEditable(false);
+-        addNotify();
+-        pack();
+-
+-        int scrWidth = (int) Toolkit.getDefaultToolkit().getScreenSize()
+-                .getWidth();
+-        int scrHeight = (int) Toolkit.getDefaultToolkit().getScreenSize()
+-                .getHeight();
+-
+-        setLocation((scrWidth - getWidth()) / 2, (scrHeight - getHeight()) / 2);
+-
+-        setVisible(visible);
+-
+-    }
+-
+-    public static void start(boolean visible) {
+-        new JMFInit(null, visible);
+-    }
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/demo/Demo.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/demo/Demo.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/demo/Demo.java	(working copy)
+@@ -1,174 +0,0 @@
+-/**
+- * $RCSfile: Demo.java,v $
+- * $Revision: 1.3 $
+- * $Date: 28/12/2006
+- * <p/>
+- * Copyright 2003-2006 Jive Software.
+- * <p/>
+- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- * <p/>
+- * http://www.apache.org/licenses/LICENSE-2.0
+- * <p/>
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.jivesoftware.smackx.jingle.mediaimpl.demo;
+-
+-import org.jivesoftware.smack.Connection;
+-import org.jivesoftware.smack.XMPPConnection;
+-import org.jivesoftware.smack.XMPPException;
+-import org.jivesoftware.smackx.jingle.JingleManager;
+-import org.jivesoftware.smackx.jingle.JingleSession;
+-import org.jivesoftware.smackx.jingle.JingleSessionRequest;
+-import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener;
+-import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
+-import org.jivesoftware.smackx.jingle.mediaimpl.jspeex.SpeexMediaManager;
+-import org.jivesoftware.smackx.jingle.mediaimpl.sshare.ScreenShareMediaManager;
+-import org.jivesoftware.smackx.jingle.nat.ICETransportManager;
+-import org.jivesoftware.smackx.jingle.nat.JingleTransportManager;
+-
+-import javax.swing.*;
+-import java.awt.event.ActionEvent;
+-import java.util.ArrayList;
+-import java.util.List;
+-
+-/**
+- * Jingle Demo Application. It register in a XMPP Server and let users place calls using a full JID and auto-receive calls.
+- * Parameters: Server User Pass.
+- */
+-public class Demo extends JFrame {
+-
+-    private JingleTransportManager transportManager = null;
+-    private Connection xmppConnection = null;
+-
+-    private String server = null;
+-    private String user = null;
+-    private String pass = null;
+-
+-    private JingleManager jm = null;
+-    private JingleSession incoming = null;
+-    private JingleSession outgoing = null;
+-
+-    private JTextField jid;
+-
+-    public Demo(String server, String user, String pass) {
+-
+-        this.server = server;
+-        this.user = user;
+-        this.pass = pass;
+-        
+-        if (user.equals("jeffw")) {
+-            jid = new JTextField("eowyn" + "@" + server + "/Smack");
+-        } else {
+-            jid = new JTextField("jeffw" + "@" + server + "/Smack");
+-        }
+-
+-        xmppConnection = new XMPPConnection(server);
+-        try {
+-            xmppConnection.connect();
+-            xmppConnection.login(user, pass);
+-            initialize();
+-        }
+-        catch (XMPPException e) {
+-            e.printStackTrace();
+-        }
+-    }
+-
+-    public void initialize() {
+-        ICETransportManager icetm0 = new ICETransportManager(xmppConnection, "10.47.47.53", 3478);
+-        List<JingleMediaManager> mediaManagers = new ArrayList<JingleMediaManager>();
+-        //mediaManagers.add(new JmfMediaManager(icetm0));
+-        mediaManagers.add(new SpeexMediaManager(icetm0));
+-        mediaManagers.add(new ScreenShareMediaManager(icetm0));
+-        jm = new JingleManager(xmppConnection, mediaManagers);
+-        jm.addCreationListener(icetm0);
+-
+-        jm.addJingleSessionRequestListener(new JingleSessionRequestListener() {
+-            public void sessionRequested(JingleSessionRequest request) {
+-
+-//                if (incoming != null)
+-//                    return;
+-
+-                try {
+-                    // Accept the call
+-                    incoming = request.accept();
+-
+-                    // Start the call
+-                    incoming.startIncoming();
+-                }
+-                catch (XMPPException e) {
+-                    e.printStackTrace();
+-                }
+-
+-            }
+-        });
+-        createGUI();
+-    }
+-
+-    public void createGUI() {
+-
+-        JPanel jPanel = new JPanel();
+-
+-        jPanel.add(jid);
+-
+-        jPanel.add(new JButton(new AbstractAction("Call") {
+-            public void actionPerformed(ActionEvent e) {
+-                if (outgoing != null) return;
+-                try {
+-                    outgoing = jm.createOutgoingJingleSession(jid.getText());
+-                    outgoing.startOutgoing();
+-                }
+-                catch (XMPPException e1) {
+-                    e1.printStackTrace();
+-                }
+-            }
+-        }));
+-
+-        jPanel.add(new JButton(new AbstractAction("Hangup") {
+-            public void actionPerformed(ActionEvent e) {
+-                if (outgoing != null)
+-                    try {
+-                        outgoing.terminate();
+-                    }
+-                    catch (XMPPException e1) {
+-                        e1.printStackTrace();
+-                    }
+-                    finally {
+-                        outgoing = null;
+-                    }
+-                if (incoming != null)
+-                    try {
+-                        incoming.terminate();
+-                    }
+-                    catch (XMPPException e1) {
+-                        e1.printStackTrace();
+-                    }
+-                    finally {
+-                        incoming = null;
+-                    }
+-            }
+-        }));
+-
+-        this.add(jPanel);
+-
+-    }
+-
+-    public static void main(String args[]) {
+-
+-        Demo demo = null;
+-
+-        if (args.length > 2) {
+-            demo = new Demo(args[0], args[1], args[2]);
+-            demo.pack();
+-            demo.setVisible(true);
+-            demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+-        }
+-
+-    }
+-
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareSession.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareSession.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareSession.java	(working copy)
+@@ -1,206 +0,0 @@
+-/**
+- * $RCSfile: ScreenShareSession.java,v $
+- * $Revision: 1.2 $
+- * $Date: 08/11/2006
+- * <p/>
+- * Copyright 2003-2006 Jive Software.
+- * <p/>
+- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- * <p/>
+- * http://www.apache.org/licenses/LICENSE-2.0
+- * <p/>
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.jivesoftware.smackx.jingle.mediaimpl.sshare;
+-
+-import java.awt.Rectangle;
+-import java.awt.event.WindowAdapter;
+-import java.awt.event.WindowEvent;
+-import java.io.IOException;
+-import java.net.DatagramSocket;
+-import java.net.InetAddress;
+-import java.net.ServerSocket;
+-import java.net.UnknownHostException;
+-
+-import javax.swing.JFrame;
+-import javax.swing.JPanel;
+-
+-import org.jivesoftware.smackx.jingle.JingleSession;
+-import org.jivesoftware.smackx.jingle.SmackLogger;
+-import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
+-import org.jivesoftware.smackx.jingle.media.PayloadType;
+-import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageDecoder;
+-import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageEncoder;
+-import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageReceiver;
+-import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageTransmitter;
+-import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
+-
+-/**
+- * This Class implements a complete JingleMediaSession.
+- * It sould be used to transmit and receive captured images from the Display.
+- * This Class should be automaticly controlled by JingleSession.
+- * For better NAT Traversal support this implementation don't support only receive or only transmit.
+- * To receive you MUST transmit. So the only implemented and functionally methods are startTransmit() and stopTransmit()
+- *
+- * @author Thiago Camargo
+- */
+-public class ScreenShareSession extends JingleMediaSession {
+-
+-	private static final SmackLogger LOGGER = SmackLogger.getLogger(ScreenShareSession.class);
+-
+-	private ImageTransmitter transmitter = null;
+-    private ImageReceiver receiver = null;
+-    private int width = 600;
+-    private int height = 600;
+-
+-    /**
+-     * Creates a org.jivesoftware.jingleaudio.jmf.AudioMediaSession with defined payload type, remote and local candidates
+-     *
+-     * @param payloadType Payload of the jmf
+-     * @param remote      the remote information. The candidate that the jmf will be sent to.
+-     * @param local       the local information. The candidate that will receive the jmf
+-     * @param locator     media locator
+-     */
+-    public ScreenShareSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local,
+-            final String locator, JingleSession jingleSession) {
+-        super(payloadType, remote, local, "Screen", jingleSession);
+-        initialize();
+-    }
+-
+-    /**
+-     * Initialize the screen share channels.
+-     */
+-    public void initialize() {
+-
+-        JingleSession session = getJingleSession();
+-        if ((session != null) && (session.getInitiator().equals(session.getConnection().getUser()))) {
+-            // If the initiator of the jingle session is us then we transmit a screen share.
+-            try {
+-                InetAddress remote = InetAddress.getByName(getRemote().getIp());
+-                transmitter = new ImageTransmitter(new DatagramSocket(getLocal().getPort()), remote, getRemote().getPort(),
+-                        new Rectangle(0, 0, width, height));
+-            } catch (Exception e) {
+-                e.printStackTrace();
+-            }
+-
+-        } else {
+-            // Otherwise we receive a screen share.
+-            JFrame window = new JFrame();
+-            JPanel jp = new JPanel();
+-            window.add(jp);
+-
+-            window.setLocation(0, 0);
+-            window.setSize(600, 600);
+-
+-            window.addWindowListener(new WindowAdapter() {
+-                public void windowClosed(WindowEvent e) {
+-                    receiver.stop();
+-                }
+-            });
+-
+-            try {
+-                receiver = new ImageReceiver(InetAddress.getByName("0.0.0.0"), getRemote().getPort(), getLocal().getPort(), width,
+-                        height);
+-                LOGGER.debug("Receiving on:" + receiver.getLocalPort());
+-            } catch (UnknownHostException e) {
+-                e.printStackTrace();
+-            }
+-
+-            jp.add(receiver);
+-            receiver.setVisible(true);
+-            window.setAlwaysOnTop(true);
+-            window.setVisible(true);
+-        }
+-    }
+-
+-    /**
+-     * Starts transmission and for NAT Traversal reasons start receiving also.
+-     */
+-    public void startTrasmit() {
+-        new Thread(transmitter).start();
+-    }
+-
+-    /**
+-     * Set transmit activity. If the active is true, the instance should trasmit.
+-     * If it is set to false, the instance should pause transmit.
+-     *
+-     * @param active active state
+-     */
+-    public void setTrasmit(boolean active) {
+-        transmitter.setTransmit(true);
+-    }
+-
+-    /**
+-     * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
+-     */
+-    public void startReceive() {
+-        // Do nothing
+-    }
+-
+-    /**
+-     * Stops transmission and for NAT Traversal reasons stop receiving also.
+-     */
+-    public void stopTrasmit() {
+-        if (transmitter != null) {
+-            transmitter.stop();
+-        }
+-    }
+-
+-    /**
+-     * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
+-     */
+-    public void stopReceive() {
+-        if (receiver != null) {
+-            receiver.stop();
+-        }
+-    }
+-
+-    /**
+-     * Obtain a free port we can use.
+-     *
+-     * @return A free port number.
+-     */
+-    protected int getFreePort() {
+-        ServerSocket ss;
+-        int freePort = 0;
+-
+-        for (int i = 0; i < 10; i++) {
+-            freePort = (int) (10000 + Math.round(Math.random() * 10000));
+-            freePort = freePort % 2 == 0 ? freePort : freePort + 1;
+-            try {
+-                ss = new ServerSocket(freePort);
+-                freePort = ss.getLocalPort();
+-                ss.close();
+-                return freePort;
+-            } catch (IOException e) {
+-                e.printStackTrace();
+-            }
+-        }
+-        try {
+-            ss = new ServerSocket(0);
+-            freePort = ss.getLocalPort();
+-            ss.close();
+-        } catch (IOException e) {
+-            e.printStackTrace();
+-        }
+-        return freePort;
+-    }
+-
+-    public void setEncoder(ImageEncoder encoder) {
+-        if (encoder != null) {
+-            this.transmitter.setEncoder(encoder);
+-        }
+-    }
+-
+-    public void setDecoder(ImageDecoder decoder) {
+-        if (decoder != null) {
+-            this.receiver.setDecoder(decoder);
+-        }
+-    }
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageTransmitter.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageTransmitter.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageTransmitter.java	(working copy)
+@@ -1,204 +0,0 @@
+-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
+-
+-import java.awt.AWTException;
+-import java.awt.Rectangle;
+-import java.awt.Robot;
+-import java.awt.image.BufferedImage;
+-import java.awt.image.PixelGrabber;
+-import java.io.ByteArrayOutputStream;
+-import java.io.IOException;
+-import java.net.DatagramPacket;
+-import java.net.DatagramSocket;
+-import java.net.InetAddress;
+-import java.util.Arrays;
+-
+-import org.jivesoftware.smackx.jingle.SmackLogger;
+-
+-/**
+- * UDP Image Receiver.
+- * It uses PNG Tiles into UDP packets.
+- *
+- * @author Thiago Rocha Camargo
+- */
+-public class ImageTransmitter implements Runnable {
+-
+-	private static final SmackLogger LOGGER = SmackLogger.getLogger(ImageTransmitter.class);
+-
+-	private Robot robot;
+-    private InetAddress localHost;
+-    private InetAddress remoteHost;
+-    private int localPort;
+-    private int remotePort;
+-    public static final int tileWidth = 25;
+-    private boolean on = true;
+-    private boolean transmit = false;
+-    private DatagramSocket socket;
+-    private Rectangle area;
+-    private int tiles[][][];
+-    private int maxI;
+-    private int maxJ;
+-    private ImageEncoder encoder;
+-    public final static int KEYFRAME = 10;
+-
+-    public ImageTransmitter(DatagramSocket socket, InetAddress remoteHost, int remotePort, Rectangle area) {
+-
+-        try {
+-            robot = new Robot();
+-
+-            maxI = (int) Math.ceil(area.getWidth() / tileWidth);
+-            maxJ = (int) Math.ceil(area.getHeight() / tileWidth);
+-
+-            tiles = new int[maxI][maxJ][tileWidth * tileWidth];
+-
+-            this.area = area;
+-            this.socket = socket;
+-            localHost = socket.getLocalAddress();
+-            localPort = socket.getLocalPort();
+-            this.remoteHost = remoteHost;
+-            this.remotePort = remotePort;
+-            this.encoder = new DefaultEncoder();
+-
+-            transmit = true;
+-
+-        }
+-        catch (AWTException e) {
+-            e.printStackTrace();
+-        }
+-
+-    }
+-
+-    public void start() {
+-        byte buf[] = new byte[1024];
+-        final DatagramPacket p = new DatagramPacket(buf, 1024);
+-
+-        int keyframe = 0;
+-
+-        while (on) {
+-            if (transmit) {
+-
+-                BufferedImage capture = robot.createScreenCapture(area);
+-
+-                QuantizeFilter filter = new QuantizeFilter();
+-                capture = filter.filter(capture, null);
+-
+-                long trace = System.currentTimeMillis();
+-
+-                if (++keyframe > KEYFRAME) {
+-                    keyframe = 0;
+-                }
+-                LOGGER.debug("KEYFRAME:" + keyframe);
+-
+-                for (int i = 0; i < maxI; i++) {
+-                    for (int j = 0; j < maxJ; j++) {
+-
+-                        final BufferedImage bufferedImage = capture.getSubimage(i * tileWidth, j * tileWidth, tileWidth, tileWidth);
+-
+-                        int pixels[] = new int[tileWidth * tileWidth];
+-
+-                        PixelGrabber pg = new PixelGrabber(bufferedImage, 0, 0, tileWidth, tileWidth, pixels, 0, tileWidth);
+-
+-                        try {
+-                            if (pg.grabPixels()) {
+-
+-                                if (keyframe == KEYFRAME || !Arrays.equals(tiles[i][j], pixels)) {
+-
+-                                    ByteArrayOutputStream baos = encoder.encode(bufferedImage);
+-
+-                                    if (baos != null) {
+-
+-                                        try {
+-
+-                                            Thread.sleep(1);
+-
+-                                            baos.write(i);
+-                                            baos.write(j);
+-
+-                                            byte[] bytesOut = baos.toByteArray();
+-
+-                                            if (bytesOut.length > 1000)
+-                                                LOGGER.error("Bytes out > 1000. Equals " + bytesOut.length);
+-
+-                                            p.setData(bytesOut);
+-                                            p.setAddress(remoteHost);
+-                                            p.setPort(remotePort);
+-
+-                                            try {
+-                                                socket.send(p);
+-                                            }
+-                                            catch (IOException e) {
+-                                                e.printStackTrace();
+-                                            }
+-
+-                                            tiles[i][j] = pixels;
+-
+-                                        }
+-                                        catch (Exception e) {
+-                                        }
+-
+-                                    }
+-
+-                                }
+-
+-                            }
+-                        }
+-                        catch (InterruptedException e) {
+-                            e.printStackTrace();
+-                        }
+-                    }
+-                }
+-
+-                trace = (System.currentTimeMillis() - trace);
+-                LOGGER.debug("Loop Time:" + trace);
+-
+-                if (trace < 500) {
+-                    try {
+-                        Thread.sleep(500 - trace);
+-                    }
+-                    catch (InterruptedException e) {
+-                        e.printStackTrace();
+-                    }
+-                }
+-            }
+-        }
+-    }
+-
+-    public void run() {
+-        start();
+-    }
+-
+-    /**
+-     * Set Transmit Enabled/Disabled
+-     *
+-     * @param transmit boolean Enabled/Disabled
+-     */
+-    public void setTransmit(boolean transmit) {
+-        this.transmit = transmit;
+-    }
+-
+-    /**
+-     * Get the encoder used to encode Images Tiles
+-     *
+-     * @return encoder
+-     */
+-    public ImageEncoder getEncoder() {
+-        return encoder;
+-    }
+-
+-    /**
+-     * Set the encoder used to encode Image Tiles
+-     *
+-     * @param encoder encoder
+-     */
+-    public void setEncoder(ImageEncoder encoder) {
+-        this.encoder = encoder;
+-    }
+-
+-    /**
+-     * Stops Transmitter
+-     */
+-    public void stop() {
+-        this.transmit = false;
+-        this.on = false;
+-        socket.close();
+-    }
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageEncoder.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageEncoder.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageEncoder.java	(working copy)
+@@ -1,13 +0,0 @@
+-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
+-
+-import java.awt.image.BufferedImage;
+-import java.io.ByteArrayOutputStream;
+-
+-/**
+- * Image Encoder Interface use this interface if you want to change the default encoder
+-  *
+- * @author Thiago Rocha Camargo
+- */
+-public interface ImageEncoder {
+-    public ByteArrayOutputStream encode(BufferedImage bufferedImage);
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/PixelUtils.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/PixelUtils.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/PixelUtils.java	(working copy)
+@@ -1,223 +0,0 @@
+-/*
+-Copyright 2006 Jerry Huxtable
+-
+-Licensed under the Apache License, Version 2.0 (the "License");
+-you may not use this file except in compliance with the License.
+-You may obtain a copy of the License at
+-
+-   http://www.apache.org/licenses/LICENSE-2.0
+-
+-Unless required by applicable law or agreed to in writing, software
+-distributed under the License is distributed on an "AS IS" BASIS,
+-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-See the License for the specific language governing permissions and
+-limitations under the License.
+-*/
+-
+-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
+-
+-import java.awt.*;
+-import java.util.Random;
+-
+-/**
+- * Some more useful math functions for image processing.
+- * These are becoming obsolete as we move to Java2D. Use MiscComposite instead.
+- */
+-public class PixelUtils {
+-
+-	public final static int REPLACE = 0;
+-	public final static int NORMAL = 1;
+-	public final static int MIN = 2;
+-	public final static int MAX = 3;
+-	public final static int ADD = 4;
+-	public final static int SUBTRACT = 5;
+-	public final static int DIFFERENCE = 6;
+-	public final static int MULTIPLY = 7;
+-	public final static int HUE = 8;
+-	public final static int SATURATION = 9;
+-	public final static int VALUE = 10;
+-	public final static int COLOR = 11;
+-	public final static int SCREEN = 12;
+-	public final static int AVERAGE = 13;
+-	public final static int OVERLAY = 14;
+-	public final static int CLEAR = 15;
+-	public final static int EXCHANGE = 16;
+-	public final static int DISSOLVE = 17;
+-	public final static int DST_IN = 18;
+-	public final static int ALPHA = 19;
+-	public final static int ALPHA_TO_GRAY = 20;
+-
+-	private static Random randomGenerator = new Random();
+-
+-	/**
+-	 * Clamp a value to the range 0..255
+-	 */
+-	public static int clamp(int c) {
+-		if (c < 0)
+-			return 0;
+-		if (c > 255)
+-			return 255;
+-		return c;
+-	}
+-
+-	public static int interpolate(int v1, int v2, float f) {
+-		return clamp((int)(v1+f*(v2-v1)));
+-	}
+-	
+-	public static int brightness(int rgb) {
+-		int r = (rgb >> 16) & 0xff;
+-		int g = (rgb >> 8) & 0xff;
+-		int b = rgb & 0xff;
+-		return (r+g+b)/3;
+-	}
+-	
+-	public static boolean nearColors(int rgb1, int rgb2, int tolerance) {
+-		int r1 = (rgb1 >> 16) & 0xff;
+-		int g1 = (rgb1 >> 8) & 0xff;
+-		int b1 = rgb1 & 0xff;
+-		int r2 = (rgb2 >> 16) & 0xff;
+-		int g2 = (rgb2 >> 8) & 0xff;
+-		int b2 = rgb2 & 0xff;
+-		return Math.abs(r1-r2) <= tolerance && Math.abs(g1-g2) <= tolerance && Math.abs(b1-b2) <= tolerance;
+-	}
+-	
+-	private final static float hsb1[] = new float[3];//FIXME-not thread safe
+-	private final static float hsb2[] = new float[3];//FIXME-not thread safe
+-	
+-	// Return rgb1 painted onto rgb2
+-	public static int combinePixels(int rgb1, int rgb2, int op) {
+-		return combinePixels(rgb1, rgb2, op, 0xff);
+-	}
+-	
+-	public static int combinePixels(int rgb1, int rgb2, int op, int extraAlpha, int channelMask) {
+-		return (rgb2 & ~channelMask) | combinePixels(rgb1 & channelMask, rgb2, op, extraAlpha);
+-	}
+-	
+-	public static int combinePixels(int rgb1, int rgb2, int op, int extraAlpha) {
+-		if (op == REPLACE)
+-			return rgb1;
+-		int a1 = (rgb1 >> 24) & 0xff;
+-		int r1 = (rgb1 >> 16) & 0xff;
+-		int g1 = (rgb1 >> 8) & 0xff;
+-		int b1 = rgb1 & 0xff;
+-		int a2 = (rgb2 >> 24) & 0xff;
+-		int r2 = (rgb2 >> 16) & 0xff;
+-		int g2 = (rgb2 >> 8) & 0xff;
+-		int b2 = rgb2 & 0xff;
+-
+-		switch (op) {
+-		case NORMAL:
+-			break;
+-		case MIN:
+-			r1 = Math.min(r1, r2);
+-			g1 = Math.min(g1, g2);
+-			b1 = Math.min(b1, b2);
+-			break;
+-		case MAX:
+-			r1 = Math.max(r1, r2);
+-			g1 = Math.max(g1, g2);
+-			b1 = Math.max(b1, b2);
+-			break;
+-		case ADD:
+-			r1 = clamp(r1+r2);
+-			g1 = clamp(g1+g2);
+-			b1 = clamp(b1+b2);
+-			break;
+-		case SUBTRACT:
+-			r1 = clamp(r2-r1);
+-			g1 = clamp(g2-g1);
+-			b1 = clamp(b2-b1);
+-			break;
+-		case DIFFERENCE:
+-			r1 = clamp(Math.abs(r1-r2));
+-			g1 = clamp(Math.abs(g1-g2));
+-			b1 = clamp(Math.abs(b1-b2));
+-			break;
+-		case MULTIPLY:
+-			r1 = clamp(r1*r2/255);
+-			g1 = clamp(g1*g2/255);
+-			b1 = clamp(b1*b2/255);
+-			break;
+-		case DISSOLVE:
+-			if ((randomGenerator.nextInt() & 0xff) <= a1) {
+-				r1 = r2;
+-				g1 = g2;
+-				b1 = b2;
+-			}
+-			break;
+-		case AVERAGE:
+-			r1 = (r1+r2)/2;
+-			g1 = (g1+g2)/2;
+-			b1 = (b1+b2)/2;
+-			break;
+-		case HUE:
+-		case SATURATION:
+-		case VALUE:
+-		case COLOR:
+-			Color.RGBtoHSB(r1, g1, b1, hsb1);
+-			Color.RGBtoHSB(r2, g2, b2, hsb2);
+-			switch (op) {
+-			case HUE:
+-				hsb2[0] = hsb1[0];
+-				break;
+-			case SATURATION:
+-				hsb2[1] = hsb1[1];
+-				break;
+-			case VALUE:
+-				hsb2[2] = hsb1[2];
+-				break;
+-			case COLOR:
+-				hsb2[0] = hsb1[0];
+-				hsb2[1] = hsb1[1];
+-				break;
+-			}
+-			rgb1 = Color.HSBtoRGB(hsb2[0], hsb2[1], hsb2[2]);
+-			r1 = (rgb1 >> 16) & 0xff;
+-			g1 = (rgb1 >> 8) & 0xff;
+-			b1 = rgb1 & 0xff;
+-			break;
+-		case SCREEN:
+-			r1 = 255 - ((255 - r1) * (255 - r2)) / 255;
+-			g1 = 255 - ((255 - g1) * (255 - g2)) / 255;
+-			b1 = 255 - ((255 - b1) * (255 - b2)) / 255;
+-			break;
+-		case OVERLAY:
+-			int m, s;
+-			s = 255 - ((255 - r1) * (255 - r2)) / 255;
+-			m = r1 * r2 / 255;
+-			r1 = (s * r1 + m * (255 - r1)) / 255;
+-			s = 255 - ((255 - g1) * (255 - g2)) / 255;
+-			m = g1 * g2 / 255;
+-			g1 = (s * g1 + m * (255 - g1)) / 255;
+-			s = 255 - ((255 - b1) * (255 - b2)) / 255;
+-			m = b1 * b2 / 255;
+-			b1 = (s * b1 + m * (255 - b1)) / 255;
+-			break;
+-		case CLEAR:
+-			r1 = g1 = b1 = 0xff;
+-			break;
+-		case DST_IN:
+-			r1 = clamp((r2*a1)/255);
+-			g1 = clamp((g2*a1)/255);
+-			b1 = clamp((b2*a1)/255);
+-			a1 = clamp((a2*a1)/255);
+-			return (a1 << 24) | (r1 << 16) | (g1 << 8) | b1;
+-		case ALPHA:
+-			a1 = a1*a2/255;
+-			return (a1 << 24) | (r2 << 16) | (g2 << 8) | b2;
+-		case ALPHA_TO_GRAY:
+-			int na = 255-a1;
+-			return (a1 << 24) | (na << 16) | (na << 8) | na;
+-		}
+-		if (extraAlpha != 0xff || a1 != 0xff) {
+-			a1 = a1*extraAlpha/255;
+-			int a3 = (255-a1)*a2/255;
+-			r1 = clamp((r1*a1+r2*a3)/255);
+-			g1 = clamp((g1*a1+g2*a3)/255);
+-			b1 = clamp((b1*a1+b2*a3)/255);
+-			a1 = clamp(a1+a3);
+-		}
+-		return (a1 << 24) | (r1 << 16) | (g1 << 8) | b1;
+-	}
+-
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/Quantizer.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/Quantizer.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/Quantizer.java	(working copy)
+@@ -1,53 +0,0 @@
+-/*
+-Copyright 2006 Jerry Huxtable
+-
+-Licensed under the Apache License, Version 2.0 (the "License");
+-you may not use this file except in compliance with the License.
+-You may obtain a copy of the License at
+-
+-   http://www.apache.org/licenses/LICENSE-2.0
+-
+-Unless required by applicable law or agreed to in writing, software
+-distributed under the License is distributed on an "AS IS" BASIS,
+-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-See the License for the specific language governing permissions and
+-limitations under the License.
+-*/
+-
+-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
+-
+-/**
+- * The interface for an image quantizer. The addColor method is called (repeatedly
+- * if necessary) with all the image pixels. A color table can then be returned by 
+- * calling the buildColorTable method.
+- */
+-public interface Quantizer {
+-	/**
+-	 * Initialize the quantizer. This should be called before adding any pixels.
+-	 * @param numColors the number of colors we're quantizing to.
+-	 */
+-	public void setup(int numColors);
+-	
+-	/**
+-	 * Add pixels to the quantizer.
+-	 * @param pixels the array of ARGB pixels
+-	 * @param offset the offset into the array
+-	 * @param count the count of pixels
+-	 */
+-	public void addPixels(int[] pixels, int offset, int count);
+-	
+-	/**
+-	 * Build a color table from the added pixels.
+-	 * @return an array of ARGB pixels representing a color table
+-	 */
+-	public int[] buildColorTable();
+-	
+-	/**
+-	 * Using the previously-built color table, return the index into that table for a pixel.
+-	 * This is guaranteed to return a valid index - returning the index of a color closer
+-	 * to that requested if necessary. 
+-	 * @param rgb the pixel to find
+-	 * @return the pixel's index in the color table
+-	 */
+-	public int getIndexForColor(int rgb);
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultEncoder.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultEncoder.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultEncoder.java	(working copy)
+@@ -1,24 +0,0 @@
+-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
+-
+-import javax.imageio.ImageIO;
+-import java.awt.image.BufferedImage;
+-import java.io.ByteArrayOutputStream;
+-import java.io.IOException;
+-
+-/**
+- * Implements a default PNG Encoder
+- */
+-public class DefaultEncoder implements ImageEncoder{
+-
+-    public ByteArrayOutputStream encode(BufferedImage bufferedImage) {
+-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+-        try {
+-            ImageIO.write(bufferedImage, "png", baos);
+-        }
+-        catch (IOException e) {
+-            e.printStackTrace();
+-            baos = null;
+-        }
+-        return baos;
+-    }
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/QuantizeFilter.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/QuantizeFilter.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/QuantizeFilter.java	(working copy)
+@@ -1,178 +0,0 @@
+-/*
+-Copyright 2006 Jerry Huxtable
+-
+-Licensed under the Apache License, Version 2.0 (the "License");
+-you may not use this file except in compliance with the License.
+-You may obtain a copy of the License at
+-
+-   http://www.apache.org/licenses/LICENSE-2.0
+-
+-Unless required by applicable law or agreed to in writing, software
+-distributed under the License is distributed on an "AS IS" BASIS,
+-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-See the License for the specific language governing permissions and
+-limitations under the License.
+-*/
+-
+-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
+-
+-import java.awt.*;
+-
+-/**
+- * A filter which quantizes an image to a set number of colors - useful for producing
+- * images which are to be encoded using an index color model. The filter can perform
+- * Floyd-Steinberg error-diffusion dithering if required. At present, the quantization
+- * is done using an octtree algorithm but I eventually hope to add more quantization
+- * methods such as median cut. Note: at present, the filter produces an image which
+- * uses the RGB color model (because the application it was written for required it).
+- * I hope to extend it to produce an IndexColorModel by request.
+- */
+-public class QuantizeFilter extends WholeImageFilter {
+-
+-	/**
+-	 * Floyd-Steinberg dithering matrix.
+-	 */
+-	protected final static int[] matrix = {
+-	 	 0, 0, 0,
+-	 	 0, 0, 7,
+-	 	 3, 5, 1,
+-	};
+-	private int sum = 3+5+7+1;
+-
+-	private boolean dither;
+-	private int numColors = 256;
+-	private boolean serpentine = true;
+-
+-	/**
+-	 * Set the number of colors to quantize to.
+-	 * @param numColors the number of colors. The default is 256.
+-	 */
+-	public void setNumColors(int numColors) {
+-		this.numColors = Math.min(Math.max(numColors, 8), 256);
+-	}
+-
+-	/**
+-	 * Get the number of colors to quantize to.
+-	 * @return the number of colors.
+-	 */
+-	public int getNumColors() {
+-		return numColors;
+-	}
+-
+-	/**
+-	 * Set whether to use dithering or not. If not, the image is posterized.
+-	 * @param dither true to use dithering
+-	 */
+-	public void setDither(boolean dither) {
+-		this.dither = dither;
+-	}
+-
+-	/**
+-	 * Return the dithering setting
+-	 * @return the current setting
+-	 */
+-	public boolean getDither() {
+-		return dither;
+-	}
+-
+-	/**
+-	 * Set whether to use a serpentine pattern for return or not. This can reduce 'avalanche' artifacts in the output.
+-	 * @param serpentine true to use serpentine pattern
+-	 */
+-	public void setSerpentine(boolean serpentine) {
+-		this.serpentine = serpentine;
+-	}
+-	
+-	/**
+-	 * Return the serpentine setting
+-	 * @return the current setting
+-	 */
+-	public boolean getSerpentine() {
+-		return serpentine;
+-	}
+-	
+-	public void quantize(int[] inPixels, int[] outPixels, int width, int height, int numColors, boolean dither, boolean serpentine) {
+-		int count = width*height;
+-		Quantizer quantizer = new OctTreeQuantizer();
+-		quantizer.setup(numColors);
+-		quantizer.addPixels(inPixels, 0, count);
+-		int[] table =  quantizer.buildColorTable();
+-
+-		if (!dither) {
+-			for (int i = 0; i < count; i++)
+-				outPixels[i] = table[quantizer.getIndexForColor(inPixels[i])];
+-		} else {
+-			int index = 0;
+-			for (int y = 0; y < height; y++) {
+-				boolean reverse = serpentine && (y & 1) == 1;
+-				int direction;
+-				if (reverse) {
+-					index = y*width+width-1;
+-					direction = -1;
+-				} else {
+-					index = y*width;
+-					direction = 1;
+-				}
+-				for (int x = 0; x < width; x++) {
+-					int rgb1 = inPixels[index];
+-					int rgb2 = table[quantizer.getIndexForColor(rgb1)];
+-
+-					outPixels[index] = rgb2;
+-
+-					int r1 = (rgb1 >> 16) & 0xff;
+-					int g1 = (rgb1 >> 8) & 0xff;
+-					int b1 = rgb1 & 0xff;
+-
+-					int r2 = (rgb2 >> 16) & 0xff;
+-					int g2 = (rgb2 >> 8) & 0xff;
+-					int b2 = rgb2 & 0xff;
+-
+-					int er = r1-r2;
+-					int eg = g1-g2;
+-					int eb = b1-b2;
+-
+-					for (int i = -1; i <= 1; i++) {
+-						int iy = i+y;
+-						if (0 <= iy && iy < height) {
+-							for (int j = -1; j <= 1; j++) {
+-								int jx = j+x;
+-								if (0 <= jx && jx < width) {
+-									int w;
+-									if (reverse)
+-										w = matrix[(i+1)*3-j+1];
+-									else
+-										w = matrix[(i+1)*3+j+1];
+-									if (w != 0) {
+-										int k = reverse ? index - j : index + j;
+-										rgb1 = inPixels[k];
+-										r1 = (rgb1 >> 16) & 0xff;
+-										g1 = (rgb1 >> 8) & 0xff;
+-										b1 = rgb1 & 0xff;
+-										r1 += er * w/sum;
+-										g1 += eg * w/sum;
+-										b1 += eb * w/sum;
+-										inPixels[k] = (PixelUtils.clamp(r1) << 16) | (PixelUtils.clamp(g1) << 8) | PixelUtils.clamp(b1);
+-									}
+-								}
+-							}
+-						}
+-					}
+-					index += direction;
+-				}
+-			}
+-		}
+-	}
+-
+-	protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) {
+-		int[] outPixels = new int[width*height];
+-		
+-		quantize(inPixels, outPixels, width, height, numColors, dither, serpentine);
+-
+-		return outPixels;
+-	}
+-
+-	public String toString() {
+-		return "Colors/Quantize...";
+-	}
+-	
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageReceiver.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageReceiver.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageReceiver.java	(working copy)
+@@ -1,150 +0,0 @@
+-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
+-
+-import java.awt.*;
+-import java.awt.image.BufferedImage;
+-import java.io.ByteArrayInputStream;
+-import java.io.IOException;
+-import java.net.DatagramPacket;
+-import java.net.DatagramSocket;
+-import java.net.InetAddress;
+-import java.net.SocketException;
+-
+-/**
+- * UDP Image Receiver.
+- * It uses PNG Tiles into UDP packets.
+- *
+- * @author Thiago Rocha Camargo
+- */
+-public class ImageReceiver extends Canvas {
+-
+-    private boolean on = true;
+-    private DatagramSocket socket;
+-    private BufferedImage tiles[][];
+-    private static final int tileWidth = ImageTransmitter.tileWidth;
+-    private InetAddress localHost;
+-    private InetAddress remoteHost;
+-    private int localPort;
+-    private int remotePort;
+-    private ImageDecoder decoder;
+-
+-    public ImageReceiver(final InetAddress remoteHost, final int remotePort, final int localPort, int width, int height) {
+-        tiles = new BufferedImage[width][height];
+-
+-        try {
+-
+-            socket = new DatagramSocket(localPort);
+-            localHost = socket.getLocalAddress();
+-            this.remoteHost = remoteHost;
+-            this.remotePort = remotePort;
+-            this.localPort = localPort;
+-            this.decoder = new DefaultDecoder();
+-
+-            new Thread(new Runnable() {
+-                public void run() {
+-                    byte buf[] = new byte[1024];
+-                    DatagramPacket p = new DatagramPacket(buf, 1024);
+-                    try {
+-                        while (on) {
+-                            socket.receive(p);
+-
+-                            int length = p.getLength();
+-
+-                            BufferedImage bufferedImage = decoder.decode(new ByteArrayInputStream(p.getData(), 0, length - 2));
+-
+-                            if (bufferedImage != null) {
+-
+-                                int x = p.getData()[length - 2];
+-                                int y = p.getData()[length - 1];
+-
+-                                drawTile(x, y, bufferedImage);
+-
+-                            }
+-
+-                        }
+-                    }
+-                    catch (IOException e) {
+-                        e.printStackTrace();
+-                    }
+-                }
+-            }).start();
+-
+-            new Thread(new Runnable() {
+-                public void run() {
+-                    byte buf[] = new byte[1024];
+-                    DatagramPacket p = new DatagramPacket(buf, 1024);
+-                    try {
+-                        while (on) {
+-
+-                            p.setAddress(remoteHost);
+-                            p.setPort(remotePort);
+-                            socket.send(p);
+-
+-                            try {
+-                                Thread.sleep(1000);
+-                            }
+-                            catch (InterruptedException e) {
+-                                e.printStackTrace();
+-                            }
+-
+-                        }
+-                    }
+-                    catch (IOException e) {
+-                        e.printStackTrace();
+-                    }
+-                }
+-            }).start();
+-
+-        }
+-        catch (SocketException e) {
+-            e.printStackTrace();
+-        }
+-        this.setSize(width, height);
+-    }
+-
+-    public InetAddress getLocalHost() {
+-        return localHost;
+-    }
+-
+-    public InetAddress getRemoteHost() {
+-        return remoteHost;
+-    }
+-
+-    public int getLocalPort() {
+-        return localPort;
+-    }
+-
+-    public int getRemotePort() {
+-        return remotePort;
+-    }
+-
+-    public DatagramSocket getDatagramSocket() {
+-        return socket;
+-    }
+-
+-    public void drawTile(int x, int y, BufferedImage bufferedImage) {
+-        tiles[x][y] = bufferedImage;
+-        //repaint(x * tileWidth, y * tileWidth, tileWidth, tileWidth);
+-        this.getGraphics().drawImage(bufferedImage, tileWidth * x, tileWidth * y, this);
+-    }
+-
+-    public void paint(Graphics g) {
+-        for (int i = 0; i < tiles.length; i++) {
+-            for (int j = 0; j < tiles[0].length; j++) {
+-                g.drawImage(tiles[i][j], tileWidth * i, tileWidth * j, this);
+-            }
+-        }
+-    }
+-
+-    public ImageDecoder getDecoder() {
+-        return decoder;
+-    }
+-
+-    public void setDecoder(ImageDecoder decoder) {
+-        this.decoder = decoder;
+-    }
+-
+-    public void stop(){
+-        this.on=false;
+-        socket.close();
+-    }
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/WholeImageFilter.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/WholeImageFilter.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/WholeImageFilter.java	(working copy)
+@@ -1,86 +0,0 @@
+-/*
+-Copyright 2006 Jerry Huxtable
+-
+-Licensed under the Apache License, Version 2.0 (the "License");
+-you may not use this file except in compliance with the License.
+-You may obtain a copy of the License at
+-
+-   http://www.apache.org/licenses/LICENSE-2.0
+-
+-Unless required by applicable law or agreed to in writing, software
+-distributed under the License is distributed on an "AS IS" BASIS,
+-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-See the License for the specific language governing permissions and
+-limitations under the License.
+-*/
+-
+-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
+-
+-import java.awt.*;
+-import java.awt.image.BufferedImage;
+-import java.awt.image.ColorModel;
+-import java.awt.image.WritableRaster;
+-
+-/**
+- * A filter which acts as a superclass for filters which need to have the whole image in memory
+- * to do their stuff.
+- */
+-public abstract class WholeImageFilter extends AbstractBufferedImageOp {
+-
+-	/**
+-     * The output image bounds.
+-     */
+-    protected Rectangle transformedSpace;
+-
+-	/**
+-     * The input image bounds.
+-     */
+-	protected Rectangle originalSpace;
+-	
+-	/**
+-	 * Construct a WholeImageFilter.
+-	 */
+-	public WholeImageFilter() {
+-	}
+-
+-    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
+-        int width = src.getWidth();
+-        int height = src.getHeight();
+-		int type = src.getType();
+-		WritableRaster srcRaster = src.getRaster();
+-
+-		originalSpace = new Rectangle(0, 0, width, height);
+-		transformedSpace = new Rectangle(0, 0, width, height);
+-		transformSpace(transformedSpace);
+-
+-        if ( dst == null ) {
+-            ColorModel dstCM = src.getColorModel();
+-			dst = new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(transformedSpace.width, transformedSpace.height), dstCM.isAlphaPremultiplied(), null);
+-		}
+-		WritableRaster dstRaster = dst.getRaster();
+-
+-		int[] inPixels = getRGB( src, 0, 0, width, height, null );
+-		inPixels = filterPixels( width, height, inPixels, transformedSpace );
+-		setRGB( dst, 0, 0, transformedSpace.width, transformedSpace.height, inPixels );
+-
+-        return dst;
+-    }
+-
+-	/**
+-     * Calculate output bounds for given input bounds.
+-     * @param rect input and output rectangle
+-     */
+-	protected void transformSpace(Rectangle rect) {
+-	}
+-	
+-	/**
+-     * Actually filter the pixels.
+-     * @param width the image width
+-     * @param height the image height
+-     * @param inPixels the image pixels
+-     * @param transformedSpace the output bounds
+-     * @return the output pixels
+-     */
+-	protected abstract int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace );
+-}
+-
+Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/AbstractBufferedImageOp.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/AbstractBufferedImageOp.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/AbstractBufferedImageOp.java	(working copy)
+@@ -1,98 +0,0 @@
+-/*
+-Copyright 2006 Jerry Huxtable
+-
+-Licensed under the Apache License, Version 2.0 (the "License");
+-you may not use this file except in compliance with the License.
+-You may obtain a copy of the License at
+-
+-   http://www.apache.org/licenses/LICENSE-2.0
+-
+-Unless required by applicable law or agreed to in writing, software
+-distributed under the License is distributed on an "AS IS" BASIS,
+-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-See the License for the specific language governing permissions and
+-limitations under the License.
+-*/
+-
+-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
+-
+-import java.awt.*;
+-import java.awt.geom.Point2D;
+-import java.awt.geom.Rectangle2D;
+-import java.awt.image.BufferedImage;
+-import java.awt.image.BufferedImageOp;
+-import java.awt.image.ColorModel;
+-
+-/**
+- * A convenience class which implements those methods of BufferedImageOp which are rarely changed.
+- */
+-public abstract class AbstractBufferedImageOp implements BufferedImageOp, Cloneable {
+-
+-    public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel dstCM) {
+-        if ( dstCM == null )
+-            dstCM = src.getColorModel();
+-        return new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(src.getWidth(), src.getHeight()), dstCM.isAlphaPremultiplied(), null);
+-    }
+-    
+-    public Rectangle2D getBounds2D( BufferedImage src ) {
+-        return new Rectangle(0, 0, src.getWidth(), src.getHeight());
+-    }
+-    
+-    public Point2D getPoint2D( Point2D srcPt, Point2D dstPt ) {
+-        if ( dstPt == null )
+-            dstPt = new Point2D.Double();
+-        dstPt.setLocation( srcPt.getX(), srcPt.getY() );
+-        return dstPt;
+-    }
+-
+-    public RenderingHints getRenderingHints() {
+-        return null;
+-    }
+-
+-	/**
+-	 * A convenience method for getting ARGB pixels from an image. This tries to avoid the performance
+-	 * penalty of BufferedImage.getRGB unmanaging the image.
+-     * @param image   a BufferedImage object
+-     * @param x       the left edge of the pixel block
+-     * @param y       the right edge of the pixel block
+-     * @param width   the width of the pixel arry
+-     * @param height  the height of the pixel arry
+-     * @param pixels  the array to hold the returned pixels. May be null.
+-     * @return the pixels
+-     * @see #setRGB
+-     */
+-	public int[] getRGB( BufferedImage image, int x, int y, int width, int height, int[] pixels ) {
+-		int type = image.getType();
+-		if ( type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB )
+-			return (int [])image.getRaster().getDataElements( x, y, width, height, pixels );
+-		return image.getRGB( x, y, width, height, pixels, 0, width );
+-    }
+-
+-	/**
+-	 * A convenience method for setting ARGB pixels in an image. This tries to avoid the performance
+-	 * penalty of BufferedImage.setRGB unmanaging the image.
+-     * @param image   a BufferedImage object
+-     * @param x       the left edge of the pixel block
+-     * @param y       the right edge of the pixel block
+-     * @param width   the width of the pixel arry
+-     * @param height  the height of the pixel arry
+-     * @param pixels  the array of pixels to set
+-     * @see #getRGB
+-	 */
+-	public void setRGB( BufferedImage image, int x, int y, int width, int height, int[] pixels ) {
+-		int type = image.getType();
+-		if ( type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB )
+-			image.getRaster().setDataElements( x, y, width, height, pixels );
+-		else
+-			image.setRGB( x, y, width, height, pixels, 0, width );
+-    }
+-
+-	public Object clone() {
+-		try {
+-			return super.clone();
+-		}
+-		catch ( CloneNotSupportedException e ) {
+-			return null;
+-		}
+-	}
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageDecoder.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageDecoder.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageDecoder.java	(working copy)
+@@ -1,15 +0,0 @@
+-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
+-
+-import java.awt.image.BufferedImage;
+-import java.io.ByteArrayInputStream;
+-import java.io.IOException;
+-
+-/**
+- * Image Decoder Interface use this interface if you want to change the default decoder
+- *
+- * @author Thiago Rocha Camargo
+- */
+-public interface ImageDecoder {
+-
+-    public BufferedImage decode(ByteArrayInputStream stream) throws IOException;
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/OctTreeQuantizer.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/OctTreeQuantizer.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/OctTreeQuantizer.java	(working copy)
+@@ -1,287 +0,0 @@
+-/*
+-Copyright 2006 Jerry Huxtable
+-
+-Licensed under the Apache License, Version 2.0 (the "License");
+-you may not use this file except in compliance with the License.
+-You may obtain a copy of the License at
+-
+-   http://www.apache.org/licenses/LICENSE-2.0
+-
+-Unless required by applicable law or agreed to in writing, software
+-distributed under the License is distributed on an "AS IS" BASIS,
+-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-See the License for the specific language governing permissions and
+-limitations under the License.
+-*/
+-
+-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
+-
+-import java.io.PrintStream;
+-import java.util.Vector;
+-
+-import org.jivesoftware.smackx.jingle.SmackLogger;
+-
+-/**
+- * An image Quantizer based on the Octree algorithm. This is a very basic implementation
+- * at present and could be much improved by picking the nodes to reduce more carefully 
+- * (i.e. not completely at random) when I get the time.
+- */
+-public class OctTreeQuantizer implements Quantizer {
+-
+-	private static final SmackLogger LOGGER = SmackLogger.getLogger(OctTreeQuantizer.class);
+-
+-	/**
+-	 * The greatest depth the tree is allowed to reach
+-	 */
+-	final static int MAX_LEVEL = 5;
+-
+-	/**
+-	 * An Octtree node.
+-	 */
+-	class OctTreeNode {
+-		int children;
+-		int level;
+-		OctTreeNode parent;
+-		OctTreeNode leaf[] = new OctTreeNode[8];
+-		boolean isLeaf;
+-		int count;
+-		int	totalRed;
+-		int	totalGreen;
+-		int	totalBlue;
+-		int index;
+-		
+-		/**
+-		 * A debugging method which prints the tree out.
+-		 */
+-		public void list(PrintStream s, int level) {
+-			String indentStr = "";
+-			for (int i = 0; i < level; i++)
+-				indentStr += " ";
+-			if (count == 0)
+-				LOGGER.debug(indentStr + index + ": count=" + count);
+-			else
+-				LOGGER.debug(indentStr + index + ": count=" + count + " red=" + (totalRed/count) + " green=" + (totalGreen / count) + " blue=" + (totalBlue / count));
+-			for (int i = 0; i < 8; i++)
+-				if (leaf[i] != null)
+-					leaf[i].list(s, level+2);
+-		}
+-	}
+-
+-	private int nodes = 0;
+-	private OctTreeNode root;
+-	private int reduceColors;
+-	private int maximumColors;
+-	private int colors = 0;
+-	private Vector[] colorList;
+-	
+-	public OctTreeQuantizer() {
+-		setup(256);
+-		colorList = new Vector[MAX_LEVEL+1];
+-		for (int i = 0; i < MAX_LEVEL+1; i++)
+-			colorList[i] = new Vector();
+-		root = new OctTreeNode();
+-	}
+-
+-	/**
+-	 * Initialize the quantizer. This should be called before adding any pixels.
+-	 * @param numColors the number of colors we're quantizing to.
+-	 */
+-	public void setup(int numColors) {
+-		maximumColors = numColors;
+-		reduceColors = Math.max(512, numColors * 2);
+-	}
+-	
+-	/**
+-	 * Add pixels to the quantizer.
+-	 * @param pixels the array of ARGB pixels
+-	 * @param offset the offset into the array
+-	 * @param count the count of pixels
+-	 */
+-	public void addPixels(int[] pixels, int offset, int count) {
+-		for (int i = 0; i < count; i++) {
+-			insertColor(pixels[i+offset]);
+-			if (colors > reduceColors)
+-				reduceTree(reduceColors);
+-		}
+-	}	
+-
+-    /**
+-     * Get the color table index for a color.
+-     * @param rgb the color
+-     * @return the index
+-     */
+-	public int getIndexForColor(int rgb) {
+-		int red = (rgb >> 16) & 0xff;
+-		int green = (rgb >> 8) & 0xff;
+-		int blue = rgb & 0xff;
+-
+-		OctTreeNode node = root;
+-
+-		for (int level = 0; level <= MAX_LEVEL; level++) {
+-			OctTreeNode child;
+-			int bit = 0x80 >> level;
+-
+-			int index = 0;
+-			if ((red & bit) != 0)
+-				index += 4;
+-			if ((green & bit) != 0)
+-				index += 2;
+-			if ((blue & bit) != 0)
+-				index += 1;
+-
+-			child = node.leaf[index];
+-
+-			if (child == null)
+-				return node.index;
+-			else if (child.isLeaf)
+-				return child.index;
+-			else
+-				node = child;
+-		}
+-		LOGGER.debug("getIndexForColor failed");
+-		return 0;
+-	}
+-
+-	private void insertColor(int rgb) {
+-		int red = (rgb >> 16) & 0xff;
+-		int green = (rgb >> 8) & 0xff;
+-		int blue = rgb & 0xff;
+-
+-		OctTreeNode node = root;
+-
+-//		LOGGER.debug("insertColor="+Integer.toHexString(rgb));
+-		for (int level = 0; level <= MAX_LEVEL; level++) {
+-			OctTreeNode child;
+-			int bit = 0x80 >> level;
+-
+-			int index = 0;
+-			if ((red & bit) != 0)
+-				index += 4;
+-			if ((green & bit) != 0)
+-				index += 2;
+-			if ((blue & bit) != 0)
+-				index += 1;
+-
+-			child = node.leaf[index];
+-
+-			if (child == null) {
+-				node.children++;
+-
+-				child = new OctTreeNode();
+-				child.parent = node;
+-				node.leaf[index] = child;
+-				node.isLeaf = false;
+-				nodes++;
+-				colorList[level].addElement(child);
+-
+-				if (level == MAX_LEVEL) {
+-					child.isLeaf = true;
+-					child.count = 1;
+-					child.totalRed = red;
+-					child.totalGreen = green;
+-					child.totalBlue = blue;
+-					child.level = level;
+-					colors++;
+-					return;
+-				}
+-
+-				node = child;
+-			} else if (child.isLeaf) {
+-				child.count++;
+-				child.totalRed += red;
+-				child.totalGreen += green;
+-				child.totalBlue += blue;
+-				return;
+-			} else
+-				node = child;
+-		}
+-		LOGGER.debug("insertColor failed");
+-	}
+-
+-	private void reduceTree(int numColors) {
+-		for (int level = MAX_LEVEL-1; level >= 0; level--) {
+-			Vector v = colorList[level];
+-			if (v != null && v.size() > 0) {
+-				for (int j = 0; j < v.size(); j++) {
+-					OctTreeNode node = (OctTreeNode)v.elementAt(j);
+-					if (node.children > 0) {
+-						for (int i = 0; i < 8; i++) {
+-							OctTreeNode child = node.leaf[i];
+-							if (child != null) {
+-								if (!child.isLeaf)
+-									LOGGER.debug("not a leaf!");
+-								node.count += child.count;
+-								node.totalRed += child.totalRed;
+-								node.totalGreen += child.totalGreen;
+-								node.totalBlue += child.totalBlue;
+-								node.leaf[i] = null;
+-								node.children--;
+-								colors--;
+-								nodes--;
+-								colorList[level+1].removeElement(child);
+-							}
+-						}
+-						node.isLeaf = true;
+-						colors++;
+-						if (colors <= numColors)
+-							return;
+-					}
+-				}
+-			}
+-		}
+-
+-		LOGGER.debug("Unable to reduce the OctTree");
+-	}
+-
+-    /**
+-     * Build the color table.
+-     * @return the color table
+-     */
+-	public int[] buildColorTable() {
+-		int[] table = new int[colors];
+-		buildColorTable(root, table, 0);
+-		return table;
+-	}
+-	
+-	/**
+-	 * A quick way to use the quantizer. Just create a table the right size and pass in the pixels.
+-     * @param inPixels the input colors
+-     * @param table the output color table
+-     */
+-	public void buildColorTable(int[] inPixels, int[] table) {
+-		int count = inPixels.length;
+-		maximumColors = table.length;
+-		for (int i = 0; i < count; i++) {
+-			insertColor(inPixels[i]);
+-			if (colors > reduceColors)
+-				reduceTree(reduceColors);
+-		}
+-		if (colors > maximumColors)
+-			reduceTree(maximumColors);
+-		buildColorTable(root, table, 0);
+-	}
+-
+-	private int buildColorTable(OctTreeNode node, int[] table, int index) {
+-		if (colors > maximumColors)
+-			reduceTree(maximumColors);
+-
+-		if (node.isLeaf) {
+-			int count = node.count;
+-			table[index] = 0xff000000 |
+-				((node.totalRed/count) << 16) |
+-				((node.totalGreen/count) << 8) |
+-				node.totalBlue/count;
+-			node.index = index++;
+-		} else {
+-			for (int i = 0; i < 8; i++) {
+-				if (node.leaf[i] != null) {
+-					node.index = index;
+-					index = buildColorTable(node.leaf[i], table, index);
+-				}
+-			}
+-		}
+-		return index;
+-	}
+-	
+-}
+-
+Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultDecoder.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultDecoder.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/DefaultDecoder.java	(working copy)
+@@ -1,16 +0,0 @@
+-package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api;
+-
+-import javax.imageio.ImageIO;
+-import java.awt.image.BufferedImage;
+-import java.io.ByteArrayInputStream;
+-import java.io.IOException;
+-
+-/**
+- * Implements a default PNG decoder.
+- */
+-public class DefaultDecoder implements ImageDecoder {
+-
+-    public BufferedImage decode(ByteArrayInputStream stream) throws IOException {
+-        return ImageIO.read(stream);
+-    }
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareMediaManager.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareMediaManager.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/sshare/ScreenShareMediaManager.java	(working copy)
+@@ -1,115 +0,0 @@
+-/**
+- * $RCSfile: ScreenShareMediaManager.java,v $
+- * $Revision: 1.3 $
+- * $Date: 25/12/2006
+- * <p/>
+- * Copyright 2003-2006 Jive Software.
+- * <p/>
+- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- * <p/>
+- * http://www.apache.org/licenses/LICENSE-2.0
+- * <p/>
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.jivesoftware.smackx.jingle.mediaimpl.sshare;
+-
+-import org.jivesoftware.smackx.jingle.JingleSession;
+-import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
+-import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
+-import org.jivesoftware.smackx.jingle.media.PayloadType;
+-import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageDecoder;
+-import org.jivesoftware.smackx.jingle.mediaimpl.sshare.api.ImageEncoder;
+-import org.jivesoftware.smackx.jingle.nat.JingleTransportManager;
+-import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
+-
+-import java.util.ArrayList;
+-import java.util.List;
+-
+-/**
+- * Implements a JingleMediaManager for ScreenSharing.
+- * It currently uses an Audio payload Type. Which needs to be fixed in the next version.
+- *
+- * @author Thiago Camargo
+- */
+-
+-public class ScreenShareMediaManager extends JingleMediaManager {
+-
+-    public static final String MEDIA_NAME = "ScreenShare";
+-
+-    private List<PayloadType> payloads = new ArrayList<PayloadType>();
+-
+-    private ImageDecoder decoder = null;
+-    private ImageEncoder encoder = null;
+-
+-    public ScreenShareMediaManager(JingleTransportManager transportManager) {
+-        super(transportManager);
+-        setupPayloads();
+-    }
+-
+-    /**
+-     * Setup API supported Payloads
+-     */
+-    private void setupPayloads() {
+-        payloads.add(new PayloadType.Audio(30, "sshare"));
+-    }
+-
+-    /**
+-     * Return all supported Payloads for this Manager.
+-     *
+-     * @return The Payload List
+-     */
+-    public List<PayloadType> getPayloads() {
+-        return payloads;
+-    }
+-
+-    /**
+-     * Returns a new JingleMediaSession
+-     *
+-     * @param payloadType payloadType
+-     * @param remote      remote Candidate
+-     * @param local       local Candidate
+-     * @return JingleMediaSession JingleMediaSession
+-     */
+-    public JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, final JingleSession jingleSession) {
+-        ScreenShareSession session = null;
+-        session = new ScreenShareSession(payloadType, remote, local, "Screen", jingleSession);
+-        if (encoder != null) {
+-            session.setEncoder(encoder);
+-        }
+-        if (decoder != null) {
+-            session.setDecoder(decoder);
+-        }
+-        return session;
+-    }
+-
+-    public PayloadType getPreferredPayloadType() {
+-        return super.getPreferredPayloadType();
+-    }
+-
+-    public ImageDecoder getDecoder() {
+-        return decoder;
+-    }
+-
+-    public void setDecoder(ImageDecoder decoder) {
+-        this.decoder = decoder;
+-    }
+-
+-    public ImageEncoder getEncoder() {
+-        return encoder;
+-    }
+-
+-    public void setEncoder(ImageEncoder encoder) {
+-        this.encoder = encoder;
+-    }
+-    
+-    public  String getName() {
+-        return MEDIA_NAME;
+-    }
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/multi/MultiMediaManager.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/multi/MultiMediaManager.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/multi/MultiMediaManager.java	(working copy)
+@@ -1,106 +0,0 @@
+-/**
+- * $RCSfile: MultiMediaManager.java,v $
+- * $Revision: 1.3 $
+- * $Date: 25/12/2006
+- * <p/>
+- * Copyright 2003-2006 Jive Software.
+- * <p/>
+- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- * <p/>
+- * http://www.apache.org/licenses/LICENSE-2.0
+- * <p/>
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.jivesoftware.smackx.jingle.mediaimpl.multi;
+-
+-import org.jivesoftware.smackx.jingle.JingleSession;
+-import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
+-import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
+-import org.jivesoftware.smackx.jingle.media.PayloadType;
+-import org.jivesoftware.smackx.jingle.nat.JingleTransportManager;
+-import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
+-
+-import java.util.ArrayList;
+-import java.util.List;
+-
+-/**
+- * Implements a MultiMediaManager using other JingleMediaManager implementations.
+- * It supports every Codecs that JingleMediaManagers added has.
+- *
+- * @author Thiago Camargo
+- */
+-
+-public class MultiMediaManager extends JingleMediaManager {
+-
+-    public static final String MEDIA_NAME = "Multi";
+-
+-    private List<JingleMediaManager> managers = new ArrayList<JingleMediaManager>();
+-
+-    private PayloadType preferredPayloadType = null;
+-
+-    public MultiMediaManager(JingleTransportManager transportManager) {
+-        super(transportManager);
+-    }
+-
+-    public void addMediaManager(JingleMediaManager manager) {
+-        managers.add(manager);
+-    }
+-
+-    public void removeMediaManager(JingleMediaManager manager) {
+-        managers.remove(manager);
+-    }
+-
+-    /**
+-     * Return all supported Payloads for this Manager.
+-     *
+-     * @return The Payload List
+-     */
+-    public List<PayloadType> getPayloads() {
+-        List<PayloadType> list = new ArrayList<PayloadType>();
+-        if (preferredPayloadType != null) list.add(preferredPayloadType);
+-        for (JingleMediaManager manager : managers) {
+-            for (PayloadType payloadType : manager.getPayloads()) {
+-                if (!list.contains(payloadType) && !payloadType.equals(preferredPayloadType))
+-                    list.add(payloadType);
+-            }
+-        }
+-        return list;
+-    }
+-
+-    /**
+-     * Returns a new JingleMediaSession
+-     *
+-     * @param payloadType payloadType
+-     * @param remote      remote Candidate
+-     * @param local       local Candidate
+-     * @return JingleMediaSession JingleMediaSession
+-     */
+-    public JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, final JingleSession jingleSession) {
+-        for (JingleMediaManager manager : managers) {
+-            if (manager.getPayloads().contains(payloadType)) {
+-                return manager.createMediaSession(payloadType, remote, local, jingleSession);
+-            }
+-        }
+-        return null;
+-    }
+-
+-    public PayloadType getPreferredPayloadType() {
+-        if (preferredPayloadType != null) return preferredPayloadType;
+-        return super.getPreferredPayloadType();
+-    }
+-
+-    public void setPreferredPayloadType(PayloadType preferredPayloadType) {
+-        this.preferredPayloadType = preferredPayloadType;
+-    }
+-    
+-    public  String getName() {
+-        return MEDIA_NAME;
+-    }
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioMediaSession.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioMediaSession.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioMediaSession.java	(working copy)
+@@ -1,165 +0,0 @@
+-/**
+- * $RCSfile: AudioMediaSession.java,v $
+- * $Revision: 1.1 $
+- * $Date: 08/11/2006
+- * <p/>
+- * Copyright 2003-2006 Jive Software.
+- * <p/>
+- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- * <p/>
+- * http://www.apache.org/licenses/LICENSE-2.0
+- * <p/>
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.jivesoftware.smackx.jingle.mediaimpl.jmf;
+-
+-import java.io.IOException;
+-import java.net.ServerSocket;
+-
+-import javax.media.MediaLocator;
+-
+-import org.jivesoftware.smackx.jingle.JingleSession;
+-import org.jivesoftware.smackx.jingle.SmackLogger;
+-import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
+-import org.jivesoftware.smackx.jingle.media.PayloadType;
+-import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
+-
+-/**
+- * This Class implements a complete JingleMediaSession.
+- * It sould be used to transmit and receive audio captured from the Mic.
+- * This Class should be automaticly controlled by JingleSession.
+- * But you could also use in any VOIP application.
+- * For better NAT Traversal support this implementation don't support only receive or only transmit.
+- * To receive you MUST transmit. So the only implemented and functionally methods are startTransmit() and stopTransmit()
+- *
+- * @author Thiago Camargo
+- */
+-public class AudioMediaSession extends JingleMediaSession {
+-
+-	private static final SmackLogger LOGGER = SmackLogger.getLogger(AudioMediaSession.class);
+-
+-	private AudioChannel audioChannel;
+-
+-    /**
+-     * Creates a org.jivesoftware.jingleaudio.jmf.AudioMediaSession with defined payload type, remote and local candidates
+-     *
+-     * @param payloadType Payload of the jmf
+-     * @param remote      the remote information. The candidate that the jmf will be sent to.
+-     * @param local       the local information. The candidate that will receive the jmf
+-     * @param locator     media locator
+-     */
+-    public AudioMediaSession(final PayloadType payloadType, final TransportCandidate remote,
+-            final TransportCandidate local, String locator, JingleSession jingleSession) {
+-        super(payloadType, remote, local, locator==null?"dsound://":locator,jingleSession);
+-        initialize();
+-    }
+-
+-    /**
+-     * Initialize the Audio Channel to make it able to send and receive audio
+-     */
+-    public void initialize() {
+-
+-        String ip;
+-        String localIp;
+-        int localPort;
+-        int remotePort;
+-
+-        if (this.getLocal().getSymmetric() != null) {
+-            ip = this.getLocal().getIp();
+-            localIp = this.getLocal().getLocalIp();
+-            localPort = getFreePort();
+-            remotePort = this.getLocal().getSymmetric().getPort();
+-
+-            LOGGER.debug(this.getLocal().getConnection() + " " + ip + ": " + localPort + "->" + remotePort);
+-
+-        }
+-        else {
+-            ip = this.getRemote().getIp();
+-            localIp = this.getLocal().getLocalIp();
+-            localPort = this.getLocal().getPort();
+-            remotePort = this.getRemote().getPort();
+-        }
+-
+-        audioChannel = new AudioChannel(new MediaLocator(this.getMediaLocator()), localIp, ip, localPort, remotePort, AudioFormatUtils.getAudioFormat(this.getPayloadType()),this);
+-    }
+-
+-    /**
+-     * Starts transmission and for NAT Traversal reasons start receiving also.
+-     */
+-    public void startTrasmit() {
+-        audioChannel.start();
+-    }
+-
+-    /**
+-     * Set transmit activity. If the active is true, the instance should trasmit.
+-     * If it is set to false, the instance should pause transmit.
+-     *
+-     * @param active active state
+-     */
+-    public void setTrasmit(boolean active) {
+-        audioChannel.setTrasmit(active);
+-    }
+-
+-    /**
+-     * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
+-     */
+-    public void startReceive() {
+-        // Do nothing
+-    }
+-
+-    /**
+-     * Stops transmission and for NAT Traversal reasons stop receiving also.
+-     */
+-    public void stopTrasmit() {
+-        if (audioChannel != null)
+-            audioChannel.stop();
+-    }
+-
+-    /**
+-     * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
+-     */
+-    public void stopReceive() {
+-        // Do nothing
+-    }
+-
+-    /**
+-     * Obtain a free port we can use.
+-     *
+-     * @return A free port number.
+-     */
+-    protected int getFreePort() {
+-        ServerSocket ss;
+-        int freePort = 0;
+-
+-        for (int i = 0; i < 10; i++) {
+-            freePort = (int) (10000 + Math.round(Math.random() * 10000));
+-            freePort = freePort % 2 == 0 ? freePort : freePort + 1;
+-            try {
+-                ss = new ServerSocket(freePort);
+-                freePort = ss.getLocalPort();
+-                ss.close();
+-                return freePort;
+-            }
+-            catch (IOException e) {
+-                e.printStackTrace();
+-            }
+-        }
+-        try {
+-            ss = new ServerSocket(0);
+-            freePort = ss.getLocalPort();
+-            ss.close();
+-        }
+-        catch (IOException e) {
+-            e.printStackTrace();
+-        }
+-        return freePort;
+-    }
+-
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioReceiver.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioReceiver.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioReceiver.java	(working copy)
+@@ -1,171 +0,0 @@
+-/**
+- * $RCSfile: AudioReceiver.java,v $
+- * $Revision: 1.1 $
+- * $Date: 08/11/2006
+- * <p/>
+- * Copyright 2003-2006 Jive Software.
+- * <p/>
+- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- * <p/>
+- * http://www.apache.org/licenses/LICENSE-2.0
+- * <p/>
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.jivesoftware.smackx.jingle.mediaimpl.jmf;
+-
+-import javax.media.ControllerErrorEvent;
+-import javax.media.ControllerEvent;
+-import javax.media.ControllerListener;
+-import javax.media.Player;
+-import javax.media.RealizeCompleteEvent;
+-import javax.media.protocol.DataSource;
+-import javax.media.rtp.Participant;
+-import javax.media.rtp.RTPControl;
+-import javax.media.rtp.ReceiveStream;
+-import javax.media.rtp.ReceiveStreamListener;
+-import javax.media.rtp.SessionListener;
+-import javax.media.rtp.event.ByeEvent;
+-import javax.media.rtp.event.NewParticipantEvent;
+-import javax.media.rtp.event.NewReceiveStreamEvent;
+-import javax.media.rtp.event.ReceiveStreamEvent;
+-import javax.media.rtp.event.RemotePayloadChangeEvent;
+-import javax.media.rtp.event.SessionEvent;
+-import javax.media.rtp.event.StreamMappedEvent;
+-
+-import org.jivesoftware.smackx.jingle.SmackLogger;
+-import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
+-
+-/**
+- * This class implements receive methods and listeners to be used in AudioChannel
+- *
+- * @author Thiago Camargo
+- */
+-public class AudioReceiver implements ReceiveStreamListener, SessionListener,
+-        ControllerListener {
+-
+-	private static final SmackLogger LOGGER = SmackLogger.getLogger(AudioReceiver.class);
+-
+-	boolean dataReceived = false;
+-
+-    Object dataSync;
+-    JingleMediaSession jingleMediaSession;
+-
+-    public AudioReceiver(final Object dataSync, final JingleMediaSession jingleMediaSession) {
+-        this.dataSync = dataSync;
+-        this.jingleMediaSession = jingleMediaSession;
+-    }
+-
+-    /**
+-     * JingleSessionListener.
+-     */
+-    public synchronized void update(SessionEvent evt) {
+-        if (evt instanceof NewParticipantEvent) {
+-            Participant p = ((NewParticipantEvent) evt).getParticipant();
+-            LOGGER.error("  - A new participant had just joined: " + p.getCNAME());
+-        }
+-    }
+-
+-    /**
+-     * ReceiveStreamListener
+-     */
+-    public synchronized void update(ReceiveStreamEvent evt) {
+-
+-        Participant participant = evt.getParticipant();    // could be null.
+-        ReceiveStream stream = evt.getReceiveStream();  // could be null.
+-
+-        if (evt instanceof RemotePayloadChangeEvent) {
+-            LOGGER.error("  - Received an RTP PayloadChangeEvent.");
+-            LOGGER.error("Sorry, cannot handle payload change.");
+-
+-        }
+-        else if (evt instanceof NewReceiveStreamEvent) {
+-
+-            try {
+-                stream = evt.getReceiveStream();
+-                DataSource ds = stream.getDataSource();
+-
+-                // Find out the formats.
+-                RTPControl ctl = (RTPControl) ds.getControl("javax.jmf.rtp.RTPControl");
+-                if (ctl != null) {
+-                    LOGGER.error("  - Recevied new RTP stream: " + ctl.getFormat());
+-                }
+-                else
+-                    LOGGER.error("  - Recevied new RTP stream");
+-
+-                if (participant == null)
+-                    LOGGER.error("      The sender of this stream had yet to be identified.");
+-                else {
+-                    LOGGER.error("      The stream comes from: " + participant.getCNAME());
+-                }
+-
+-                // create a player by passing datasource to the Media Manager
+-                Player p = javax.media.Manager.createPlayer(ds);
+-                if (p == null)
+-                    return;
+-
+-                p.addControllerListener(this);
+-                p.realize();
+-                jingleMediaSession.mediaReceived(participant != null ? participant.getCNAME() : "");
+-
+-                // Notify intialize() that a new stream had arrived.
+-                synchronized (dataSync) {
+-                    dataReceived = true;
+-                    dataSync.notifyAll();
+-                }
+-
+-            }
+-            catch (Exception e) {
+-                LOGGER.error("NewReceiveStreamEvent exception " + e.getMessage());
+-                return;
+-            }
+-
+-        }
+-        else if (evt instanceof StreamMappedEvent) {
+-
+-            if (stream != null && stream.getDataSource() != null) {
+-                DataSource ds = stream.getDataSource();
+-                // Find out the formats.
+-                RTPControl ctl = (RTPControl) ds.getControl("javax.jmf.rtp.RTPControl");
+-                LOGGER.error("  - The previously unidentified stream ");
+-                if (ctl != null)
+-                    LOGGER.error("      " + ctl.getFormat());
+-                LOGGER.error("      had now been identified as sent by: " + participant.getCNAME());
+-            }
+-        }
+-        else if (evt instanceof ByeEvent) {
+-
+-            LOGGER.error("  - Got \"bye\" from: " + participant.getCNAME());
+-
+-        }
+-
+-    }
+-
+-    /**
+-     * ControllerListener for the Players.
+-     */
+-    public synchronized void controllerUpdate(ControllerEvent ce) {
+-
+-        Player p = (Player) ce.getSourceController();
+-
+-        if (p == null)
+-            return;
+-
+-        // Get this when the internal players are realized.
+-        if (ce instanceof RealizeCompleteEvent) {
+-            p.start();
+-        }
+-
+-        if (ce instanceof ControllerErrorEvent) {
+-            p.removeControllerListener(this);
+-            LOGGER.error("Receiver internal error: " + ce);
+-        }
+-
+-    }
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/jmf/JmfMediaManager.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/jmf/JmfMediaManager.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/jmf/JmfMediaManager.java	(working copy)
+@@ -1,170 +0,0 @@
+-/**
+- * $RCSfile: JmfMediaManager.java,v $
+- * $Revision: 1.3 $
+- * $Date: 08/11/2006
+- * <p/>
+- * Copyright 2003-2006 Jive Software.
+- * <p/>
+- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- * <p/>
+- * http://www.apache.org/licenses/LICENSE-2.0
+- * <p/>
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.jivesoftware.smackx.jingle.mediaimpl.jmf;
+-
+-import java.io.File;
+-import java.io.IOException;
+-import java.util.ArrayList;
+-import java.util.List;
+-
+-import org.jivesoftware.smackx.jingle.JingleSession;
+-import org.jivesoftware.smackx.jingle.SmackLogger;
+-import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
+-import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
+-import org.jivesoftware.smackx.jingle.media.PayloadType;
+-import org.jivesoftware.smackx.jingle.mediaimpl.JMFInit;
+-import org.jivesoftware.smackx.jingle.nat.JingleTransportManager;
+-import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
+-
+-/**
+- * Implements a jingleMediaManager using JMF based API.
+- * It supports GSM and G723 codecs.
+- * <i>This API only currently works on windows and Mac.</i>
+- *
+- * @author Thiago Camargo
+- */
+-public class JmfMediaManager extends JingleMediaManager {
+-
+-	private static final SmackLogger LOGGER = SmackLogger.getLogger(JmfMediaManager.class);
+-
+-	public static final String MEDIA_NAME = "JMF";
+-
+-    
+-    private List<PayloadType> payloads = new ArrayList<PayloadType>();
+-    private String mediaLocator = null;
+-
+-    /**
+-     * Creates a Media Manager instance
+-     */
+-    public JmfMediaManager(JingleTransportManager transportManager) {
+-        super(transportManager);
+-        setupPayloads();
+-    }
+-
+-    /**
+-     * Creates a Media Manager instance
+-     *
+-     * @param mediaLocator Media Locator
+-     */
+-    public JmfMediaManager(String mediaLocator, JingleTransportManager transportManager) {
+-        super(transportManager);
+-        this.mediaLocator = mediaLocator;
+-        setupPayloads();
+-    }
+-
+-    /**
+-     * Returns a new jingleMediaSession
+-     *
+-     * @param payloadType payloadType
+-     * @param remote      remote Candidate
+-     * @param local       local Candidate
+-     * @return JingleMediaSession
+-     */
+-    public JingleMediaSession createMediaSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, final JingleSession jingleSession) {
+-        return new AudioMediaSession(payloadType, remote, local, mediaLocator, jingleSession);
+-    }
+-
+-    /**
+-     * Setup API supported Payloads
+-     */
+-    private void setupPayloads() {
+-        payloads.add(new PayloadType.Audio(3, "gsm"));
+-        payloads.add(new PayloadType.Audio(4, "g723"));
+-        payloads.add(new PayloadType.Audio(0, "PCMU", 16000));
+-    }
+-
+-    /**
+-     * Return all supported Payloads for this Manager
+-     *
+-     * @return The Payload List
+-     */
+-    public List<PayloadType> getPayloads() {
+-        return payloads;
+-    }
+-
+-    /**
+-     * Return the media locator or null if not defined
+-     *
+-     * @return media locator
+-     */
+-    public String getMediaLocator() {
+-        return mediaLocator;
+-    }
+-
+-    /**
+-     * Set the media locator
+-     *
+-     * @param mediaLocator media locator or null to use default
+-     */
+-    public void setMediaLocator(String mediaLocator) {
+-        this.mediaLocator = mediaLocator;
+-    }
+-
+-    /**
+-     * Runs JMFInit the first time the application is started so that capture
+-     * devices are properly detected and initialized by JMF.
+-     */
+-    public static void setupJMF() {
+-        // .jmf is the place where we store the jmf.properties file used
+-        // by JMF. if the directory does not exist or it does not contain
+-        // a jmf.properties file. or if the jmf.properties file has 0 length
+-        // then this is the first time we're running and should continue to
+-        // with JMFInit
+-        String homeDir = System.getProperty("user.home");
+-        File jmfDir = new File(homeDir, ".jmf");
+-        String classpath = System.getProperty("java.class.path");
+-        classpath += System.getProperty("path.separator")
+-                + jmfDir.getAbsolutePath();
+-        System.setProperty("java.class.path", classpath);
+-
+-        if (!jmfDir.exists())
+-            jmfDir.mkdir();
+-
+-        File jmfProperties = new File(jmfDir, "jmf.properties");
+-
+-        if (!jmfProperties.exists()) {
+-            try {
+-                jmfProperties.createNewFile();
+-            }
+-            catch (IOException ex) {
+-                LOGGER.debug("Failed to create jmf.properties");
+-                ex.printStackTrace();
+-            }
+-        }
+-
+-        // if we're running on linux checkout that libjmutil.so is where it
+-        // should be and put it there.
+-        runLinuxPreInstall();
+-
+-        //if (jmfProperties.length() == 0) {
+-        new JMFInit(null, false);
+-        //}
+-
+-    }
+-
+-    private static void runLinuxPreInstall() {
+-        // @TODO Implement Linux Pre-Install
+-    }
+-    
+-    public  String getName() {
+-        return MEDIA_NAME;
+-    }
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioChannel.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioChannel.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioChannel.java	(working copy)
+@@ -1,553 +0,0 @@
+-/**
+- * $RCSfile: AudioChannel.java,v $
+- * $Revision: 1.1 $
+- * $Date: 08/11/2006
+- * <p/>
+- * Copyright 2003-2006 Jive Software.
+- * <p/>
+- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- * <p/>
+- * http://www.apache.org/licenses/LICENSE-2.0
+- * <p/>
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.jivesoftware.smackx.jingle.mediaimpl.jmf;
+-
+-import java.io.IOException;
+-import java.net.InetAddress;
+-import java.net.UnknownHostException;
+-import java.util.ArrayList;
+-import java.util.List;
+-
+-import javax.media.Codec;
+-import javax.media.Controller;
+-import javax.media.ControllerClosedEvent;
+-import javax.media.ControllerEvent;
+-import javax.media.ControllerListener;
+-import javax.media.Format;
+-import javax.media.MediaLocator;
+-import javax.media.NoProcessorException;
+-import javax.media.Processor;
+-import javax.media.UnsupportedPlugInException;
+-import javax.media.control.BufferControl;
+-import javax.media.control.PacketSizeControl;
+-import javax.media.control.TrackControl;
+-import javax.media.format.AudioFormat;
+-import javax.media.protocol.ContentDescriptor;
+-import javax.media.protocol.DataSource;
+-import javax.media.protocol.PushBufferDataSource;
+-import javax.media.protocol.PushBufferStream;
+-import javax.media.rtp.InvalidSessionAddressException;
+-import javax.media.rtp.RTPManager;
+-import javax.media.rtp.SendStream;
+-import javax.media.rtp.SessionAddress;
+-
+-import org.jivesoftware.smackx.jingle.SmackLogger;
+-import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
+-
+-/**
+- * An Easy to use Audio Channel implemented using JMF.
+- * It sends and receives jmf for and from desired IPs and ports.
+- * Also has a rport Symetric behavior for better NAT Traversal.
+- * It send data from a defined port and receive data in the same port, making NAT binds easier.
+- * <p/>
+- * Send from portA to portB and receive from portB in portA.
+- * <p/>
+- * Sending
+- * portA ---> portB
+- * <p/>
+- * Receiving
+- * portB ---> portA
+- * <p/>
+- * <i>Transmit and Receive are interdependents. To receive you MUST trasmit. </i>
+- *
+- * @author Thiago Camargo
+- */
+-public class AudioChannel {
+-
+-	private static final SmackLogger LOGGER = SmackLogger.getLogger(AudioChannel.class);
+-	
+-	private MediaLocator locator;
+-    private String localIpAddress;
+-    private String remoteIpAddress;
+-    private int localPort;
+-    private int portBase;
+-    private Format format;
+-
+-    private Processor processor = null;
+-    private RTPManager rtpMgrs[];
+-    private DataSource dataOutput = null;
+-    private AudioReceiver audioReceiver;
+-
+-    private List<SendStream> sendStreams = new ArrayList<SendStream>();
+-
+-    private JingleMediaSession jingleMediaSession;
+-
+-    private boolean started = false;
+-
+-    /**
+-     * Creates an Audio Channel for a desired jmf locator. For instance: new MediaLocator("dsound://")
+-     *
+-     * @param locator         media locator
+-     * @param localIpAddress  local IP address
+-     * @param remoteIpAddress remote IP address
+-     * @param localPort       local port number
+-     * @param remotePort      remote port number
+-     * @param format          audio format
+-     */
+-    public AudioChannel(MediaLocator locator,
+-            String localIpAddress,
+-            String remoteIpAddress,
+-            int localPort,
+-            int remotePort,
+-            Format format, JingleMediaSession jingleMediaSession) {
+-
+-        this.locator = locator;
+-        this.localIpAddress = localIpAddress;
+-        this.remoteIpAddress = remoteIpAddress;
+-        this.localPort = localPort;
+-        this.portBase = remotePort;
+-        this.format = format;
+-        this.jingleMediaSession = jingleMediaSession;
+-    }
+-
+-    /**
+-     * Starts the transmission. Returns null if transmission started ok.
+-     * Otherwise it returns a string with the reason why the setup failed.
+-     * Starts receive also.
+-     *
+-     * @return result description
+-     */
+-    public synchronized String start() {
+-        if (started) return null;
+-
+-        // Create a processor for the specified jmf locator
+-        String result = createProcessor();
+-        if (result != null) {
+-            started = false;
+-        }
+-
+-        // Create an RTP session to transmit the output of the
+-        // processor to the specified IP address and port no.
+-        result = createTransmitter();
+-        if (result != null) {
+-            processor.close();
+-            processor = null;
+-            started = false;
+-        }
+-        else {
+-            started = true;
+-        }
+-
+-        // Start the transmission
+-        processor.start();
+-
+-        return null;
+-    }
+-
+-    /**
+-     * Stops the transmission if already started.
+-     * Stops the receiver also.
+-     */
+-    public void stop() {
+-        if (!started) return;
+-        synchronized (this) {
+-            try {
+-                started = false;
+-                if (processor != null) {
+-                    processor.stop();
+-                    processor = null;
+-
+-                    for (RTPManager rtpMgr : rtpMgrs) {
+-                        rtpMgr.removeReceiveStreamListener(audioReceiver);
+-                        rtpMgr.removeSessionListener(audioReceiver);
+-                        rtpMgr.removeTargets("Session ended.");
+-                        rtpMgr.dispose();
+-                    }
+-
+-                    sendStreams.clear();
+-
+-                }
+-            }
+-            catch (Exception e) {
+-                e.printStackTrace();
+-            }
+-        }
+-    }
+-
+-    private String createProcessor() {
+-        if (locator == null)
+-            return "Locator is null";
+-
+-        DataSource ds;
+-
+-        try {
+-            ds = javax.media.Manager.createDataSource(locator);
+-        }
+-        catch (Exception e) {
+-            // Try JavaSound Locator as a last resort
+-            try {
+-                ds = javax.media.Manager.createDataSource(new MediaLocator("javasound://"));
+-            }
+-            catch (Exception ee) {
+-                return "Couldn't create DataSource";
+-            }
+-        }
+-
+-        // Try to create a processor to handle the input jmf locator
+-        try {
+-            processor = javax.media.Manager.createProcessor(ds);
+-        }
+-        catch (NoProcessorException npe) {
+-            npe.printStackTrace();
+-            return "Couldn't create processor";
+-        }
+-        catch (IOException ioe) {
+-            ioe.printStackTrace();
+-            return "IOException creating processor";
+-        }
+-
+-        // Wait for it to configure
+-        boolean result = waitForState(processor, Processor.Configured);
+-        if (!result){
+-            return "Couldn't configure processor";
+-        }
+-        
+-        // Get the tracks from the processor
+-        TrackControl[] tracks = processor.getTrackControls();
+-
+-        // Do we have atleast one track?
+-        if (tracks == null || tracks.length < 1){
+-            return "Couldn't find tracks in processor";
+-        }
+-
+-        // Set the output content descriptor to RAW_RTP
+-        // This will limit the supported formats reported from
+-        // Track.getSupportedFormats to only valid RTP formats.
+-        ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW_RTP);
+-        processor.setContentDescriptor(cd);
+-
+-        Format supported[];
+-        Format chosen = null;
+-        boolean atLeastOneTrack = false;
+-
+-        // Program the tracks.
+-        for (int i = 0; i < tracks.length; i++) {
+-            if (tracks[i].isEnabled()) {
+-
+-                supported = tracks[i].getSupportedFormats();
+-
+-                if (supported.length > 0) {
+-                    for (Format format : supported) {
+-                        if (format instanceof AudioFormat) {
+-                            if (this.format.matches(format))
+-                                chosen = format;
+-                        }
+-                    }
+-                    if (chosen != null) {
+-                        tracks[i].setFormat(chosen);
+-                        LOGGER.error("Track " + i + " is set to transmit as:");
+-                        LOGGER.error("  " + chosen);
+-
+-                        if (tracks[i].getFormat() instanceof AudioFormat) {
+-                            int packetRate = 20;
+-                            PacketSizeControl pktCtrl = (PacketSizeControl) processor.getControl(PacketSizeControl.class.getName());
+-                            if (pktCtrl != null) {
+-                                try {
+-                                    pktCtrl.setPacketSize(getPacketSize(tracks[i].getFormat(), packetRate));
+-                                }
+-                                catch (IllegalArgumentException e) {
+-                                    pktCtrl.setPacketSize(80);
+-                                    // Do nothing
+-                                }
+-                            }
+-
+-                            if (tracks[i].getFormat().getEncoding().equals(AudioFormat.ULAW_RTP)) {
+-                                Codec codec[] = new Codec[3];
+-
+-                                codec[0] = new com.ibm.media.codec.audio.rc.RCModule();
+-                                codec[1] = new com.ibm.media.codec.audio.ulaw.JavaEncoder();
+-                                codec[2] = new com.sun.media.codec.audio.ulaw.Packetizer();
+-                                ((com.sun.media.codec.audio.ulaw.Packetizer) codec
+-                                        [2]).setPacketSize(160);
+-
+-                                try {
+-                                    tracks[i].setCodecChain(codec);
+-                                }
+-                                catch (UnsupportedPlugInException e) {
+-                                    e.printStackTrace();
+-                                }
+-                            }
+-
+-                        }
+-
+-                        atLeastOneTrack = true;
+-                    }
+-                    else
+-                        tracks[i].setEnabled(false);
+-                }
+-                else
+-                    tracks[i].setEnabled(false);
+-            }
+-        }
+-
+-        if (!atLeastOneTrack)
+-            return "Couldn't set any of the tracks to a valid RTP format";
+-
+-        result = waitForState(processor, Controller.Realized);
+-        if (!result)
+-            return "Couldn't realize processor";
+-
+-        // Get the output data source of the processor
+-        dataOutput = processor.getDataOutput();
+-
+-        return null;
+-    }
+-
+-    /**
+-     * Get the best packet size for a given codec and a codec rate
+-     *
+-     * @param codecFormat
+-     * @param milliseconds
+-     * @return
+-     * @throws IllegalArgumentException
+-     */
+-    private int getPacketSize(Format codecFormat, int milliseconds) throws IllegalArgumentException {
+-        String encoding = codecFormat.getEncoding();
+-        if (encoding.equalsIgnoreCase(AudioFormat.GSM) ||
+-                encoding.equalsIgnoreCase(AudioFormat.GSM_RTP)) {
+-            return milliseconds * 4; // 1 byte per millisec
+-        }
+-        else if (encoding.equalsIgnoreCase(AudioFormat.ULAW) ||
+-                encoding.equalsIgnoreCase(AudioFormat.ULAW_RTP)) {
+-            return milliseconds * 8;
+-        }
+-        else {
+-            throw new IllegalArgumentException("Unknown codec type");
+-        }
+-    }
+-
+-    /**
+-     * Use the RTPManager API to create sessions for each jmf
+-     * track of the processor.
+-     *
+-     * @return description
+-     */
+-    private String createTransmitter() {
+-
+-        // Cheated.  Should have checked the type.
+-        PushBufferDataSource pbds = (PushBufferDataSource) dataOutput;
+-        PushBufferStream pbss[] = pbds.getStreams();
+-
+-        rtpMgrs = new RTPManager[pbss.length];
+-        SessionAddress localAddr, destAddr;
+-        InetAddress ipAddr;
+-        SendStream sendStream;
+-        audioReceiver = new AudioReceiver(this, jingleMediaSession);
+-        int port;
+-
+-        for (int i = 0; i < pbss.length; i++) {
+-            try {
+-                rtpMgrs[i] = RTPManager.newInstance();
+-
+-                port = portBase + 2 * i;
+-                ipAddr = InetAddress.getByName(remoteIpAddress);
+-
+-                localAddr = new SessionAddress(InetAddress.getByName(this.localIpAddress),
+-                        localPort);
+-
+-                destAddr = new SessionAddress(ipAddr, port);
+-
+-                rtpMgrs[i].addReceiveStreamListener(audioReceiver);
+-                rtpMgrs[i].addSessionListener(audioReceiver);
+-
+-                BufferControl bc = (BufferControl) rtpMgrs[i].getControl("javax.media.control.BufferControl");
+-                if (bc != null) {
+-                    int bl = 160;
+-                    bc.setBufferLength(bl);
+-                }
+-
+-                try {
+-
+-                    rtpMgrs[i].initialize(localAddr);
+-
+-                }
+-                catch (InvalidSessionAddressException e) {
+-                    // In case the local address is not allowed to read, we user another local address
+-                    SessionAddress sessAddr = new SessionAddress();
+-                    localAddr = new SessionAddress(sessAddr.getDataAddress(),
+-                            localPort);
+-                    rtpMgrs[i].initialize(localAddr);
+-                }
+-
+-                rtpMgrs[i].addTarget(destAddr);
+-
+-                LOGGER.error("Created RTP session at " + localPort + " to: " + remoteIpAddress + " " + port);
+-
+-                sendStream = rtpMgrs[i].createSendStream(dataOutput, i);
+-
+-                sendStreams.add(sendStream);
+-
+-                sendStream.start();
+-
+-            }
+-            catch (Exception e) {
+-                e.printStackTrace();
+-                return e.getMessage();
+-            }
+-        }
+-
+-        return null;
+-    }
+-
+-    /**
+-     * Set transmit activity. If the active is true, the instance should trasmit.
+-     * If it is set to false, the instance should pause transmit.
+-     *
+-     * @param active active state
+-     */
+-    public void setTrasmit(boolean active) {
+-        for (SendStream sendStream : sendStreams) {
+-            try {
+-                if (active) {
+-                    sendStream.start();
+-                    LOGGER.debug("START");
+-                }
+-                else {
+-                    sendStream.stop();
+-                    LOGGER.debug("STOP");
+-                }
+-            }
+-            catch (IOException e) {
+-                e.printStackTrace();
+-            }
+-
+-        }
+-    }
+-
+-    /**
+-     * *************************************************************
+-     * Convenience methods to handle processor's state changes.
+-     * **************************************************************
+-     */
+-
+-    private Integer stateLock = 0;
+-    private boolean failed = false;
+-
+-    Integer getStateLock() {
+-        return stateLock;
+-    }
+-
+-    void setFailed() {
+-        failed = true;
+-    }
+-
+-    private synchronized boolean waitForState(Processor p, int state) {
+-        p.addControllerListener(new StateListener());
+-        failed = false;
+-
+-        // Call the required method on the processor
+-        if (state == Processor.Configured) {
+-            p.configure();
+-        }
+-        else if (state == Processor.Realized) {
+-            p.realize();
+-        }
+-
+-        // Wait until we get an event that confirms the
+-        // success of the method, or a failure event.
+-        // See StateListener inner class
+-        while (p.getState() < state && !failed) {
+-            synchronized (getStateLock()) {
+-                try {
+-                    getStateLock().wait();
+-                }
+-                catch (InterruptedException ie) {
+-                    return false;
+-                }
+-            }
+-        }
+-
+-        return !failed;
+-    }
+-
+-    /**
+-     * *************************************************************
+-     * Inner Classes
+-     * **************************************************************
+-     */
+-
+-    class StateListener implements ControllerListener {
+-
+-        public void controllerUpdate(ControllerEvent ce) {
+-
+-            // If there was an error during configure or
+-            // realize, the processor will be closed
+-            if (ce instanceof ControllerClosedEvent)
+-                setFailed();
+-
+-            // All controller events, send a notification
+-            // to the waiting thread in waitForState method.
+-            if (ce != null) {
+-                synchronized (getStateLock()) {
+-                    getStateLock().notifyAll();
+-                }
+-            }
+-        }
+-    }
+-
+-    public static void main(String args[]) {
+-
+-        InetAddress localhost;
+-        try {
+-            localhost = InetAddress.getLocalHost();
+-
+-            AudioChannel audioChannel0 = new AudioChannel(new MediaLocator("javasound://8000"), localhost.getHostAddress(), localhost.getHostAddress(), 7002, 7020, new AudioFormat(AudioFormat.GSM_RTP), null);
+-            AudioChannel audioChannel1 = new AudioChannel(new MediaLocator("javasound://8000"), localhost.getHostAddress(), localhost.getHostAddress(), 7020, 7002, new AudioFormat(AudioFormat.GSM_RTP), null);
+-
+-            audioChannel0.start();
+-            audioChannel1.start();
+-
+-            try {
+-                Thread.sleep(5000);
+-            }
+-            catch (InterruptedException e) {
+-                e.printStackTrace();
+-            }
+-
+-            audioChannel0.setTrasmit(false);
+-            audioChannel1.setTrasmit(false);
+-
+-            try {
+-                Thread.sleep(5000);
+-            }
+-            catch (InterruptedException e) {
+-                e.printStackTrace();
+-            }
+-
+-            audioChannel0.setTrasmit(true);
+-            audioChannel1.setTrasmit(true);
+-
+-            try {
+-                Thread.sleep(5000);
+-            }
+-            catch (InterruptedException e) {
+-                e.printStackTrace();
+-            }
+-
+-            audioChannel0.stop();
+-            audioChannel1.stop();
+-
+-        }
+-        catch (UnknownHostException e) {
+-            e.printStackTrace();
+-        }
+-
+-    }
+-}
+\ No newline at end of file
+Index: org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioFormatUtils.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioFormatUtils.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioFormatUtils.java	(working copy)
+@@ -1,55 +0,0 @@
+-/**
+- * $RCSfile: AudioFormatUtils.java,v $
+- * $Revision: 1.1 $
+- * $Date: 08/11/2006
+- * <p/>
+- * Copyright 2003-2006 Jive Software.
+- * <p/>
+- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- * <p/>
+- * http://www.apache.org/licenses/LICENSE-2.0
+- * <p/>
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.jivesoftware.smackx.jingle.mediaimpl.jmf;
+-
+-import org.jivesoftware.smackx.jingle.media.PayloadType;
+-
+-import javax.media.format.AudioFormat;
+-
+-/**
+- * Audio Format Utils.
+- *
+- * @author Thiago Camargo
+- */
+-public class AudioFormatUtils {
+-
+-    /**
+-     * Return a JMF AudioFormat for a given Jingle Payload type.
+-     * Return null if the payload is not supported by this jmf API.
+-     *
+-     * @param payloadtype payloadtype
+-     * @return correspondent audioType
+-     */
+-    public static AudioFormat getAudioFormat(PayloadType payloadtype) {
+-
+-        switch (payloadtype.getId()) {
+-            case 0:
+-                return new AudioFormat(AudioFormat.ULAW_RTP);
+-            case 3:
+-                return new AudioFormat(AudioFormat.GSM_RTP);
+-            case 4:
+-                return new AudioFormat(AudioFormat.G723_RTP);
+-            default:
+-                return null;
+-        }
+-
+-    }
+-
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/jspeex/SpeexMediaManager.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/jspeex/SpeexMediaManager.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/jspeex/SpeexMediaManager.java	(working copy)
+@@ -1,134 +0,0 @@
+-/**
+- * $RCSfile: SpeexMediaManager.java,v $
+- * $Revision: 1.3 $
+- * $Date: 25/12/2006
+- * <p/>
+- * Copyright 2003-2006 Jive Software.
+- * <p/>
+- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- * <p/>
+- * http://www.apache.org/licenses/LICENSE-2.0
+- * <p/>
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.jivesoftware.smackx.jingle.mediaimpl.jspeex;
+-
+-import java.io.File;
+-import java.io.IOException;
+-import java.util.ArrayList;
+-import java.util.List;
+-
+-import org.jivesoftware.smackx.jingle.JingleSession;
+-import org.jivesoftware.smackx.jingle.SmackLogger;
+-import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
+-import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
+-import org.jivesoftware.smackx.jingle.media.PayloadType;
+-import org.jivesoftware.smackx.jingle.mediaimpl.JMFInit;
+-import org.jivesoftware.smackx.jingle.nat.JingleTransportManager;
+-import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
+-
+-/**
+- * Implements a jingleMediaManager using JMF based API and JSpeex.
+- * It supports Speex codec.
+- * <i>This API only currently works on windows.</i>
+- *
+- * @author Thiago Camargo
+- */
+-public class SpeexMediaManager extends JingleMediaManager {
+-
+-	private static final SmackLogger LOGGER = SmackLogger.getLogger(SpeexMediaManager.class);
+-
+-	public static final String MEDIA_NAME = "Speex";
+-
+-    private List<PayloadType> payloads = new ArrayList<PayloadType>();
+-
+-    public SpeexMediaManager(JingleTransportManager transportManager) {
+-        super(transportManager);
+-        setupPayloads();
+-        setupJMF();
+-    }
+-
+-    /**
+-     * Returns a new jingleMediaSession
+-     *
+-     * @param payloadType payloadType
+-     * @param remote      remote Candidate
+-     * @param local       local Candidate
+-     * @return JingleMediaSession
+-     */
+-    public JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, final JingleSession jingleSession) {
+-        return new AudioMediaSession(payloadType, remote, local, null,null);
+-    }
+-
+-    /**
+-     * Setup API supported Payloads
+-     */
+-    private void setupPayloads() {
+-        payloads.add(new PayloadType.Audio(15, "speex"));
+-    }
+-
+-    /**
+-     * Return all supported Payloads for this Manager
+-     *
+-     * @return The Payload List
+-     */
+-    public List<PayloadType> getPayloads() {
+-        return payloads;
+-    }
+-
+-    /**
+-     * Runs JMFInit the first time the application is started so that capture
+-     * devices are properly detected and initialized by JMF.
+-     */
+-    public static void setupJMF() {
+-        // .jmf is the place where we store the jmf.properties file used
+-        // by JMF. if the directory does not exist or it does not contain
+-        // a jmf.properties file. or if the jmf.properties file has 0 length
+-        // then this is the first time we're running and should continue to
+-        // with JMFInit
+-        String homeDir = System.getProperty("user.home");
+-        File jmfDir = new File(homeDir, ".jmf");
+-        String classpath = System.getProperty("java.class.path");
+-        classpath += System.getProperty("path.separator")
+-                + jmfDir.getAbsolutePath();
+-        System.setProperty("java.class.path", classpath);
+-
+-        if (!jmfDir.exists())
+-            jmfDir.mkdir();
+-
+-        File jmfProperties = new File(jmfDir, "jmf.properties");
+-
+-        if (!jmfProperties.exists()) {
+-            try {
+-                jmfProperties.createNewFile();
+-            }
+-            catch (IOException ex) {
+-                LOGGER.debug("Failed to create jmf.properties");
+-                ex.printStackTrace();
+-            }
+-        }
+-
+-        // if we're running on linux checkout that libjmutil.so is where it
+-        // should be and put it there.
+-        runLinuxPreInstall();
+-
+-        if (jmfProperties.length() == 0) {
+-            new JMFInit(null, false);
+-        }
+-
+-    }
+-
+-    private static void runLinuxPreInstall() {
+-        // @TODO Implement Linux Pre-Install
+-    }
+-    
+-    public String getName() {
+-        return MEDIA_NAME;
+-    }
+-}
+Index: org/jivesoftware/smackx/jingle/mediaimpl/jspeex/AudioMediaSession.java
+===================================================================
+--- org/jivesoftware/smackx/jingle/mediaimpl/jspeex/AudioMediaSession.java	(revision 11644)
++++ org/jivesoftware/smackx/jingle/mediaimpl/jspeex/AudioMediaSession.java	(working copy)
+@@ -1,245 +0,0 @@
+-/**
+- * $RCSfile: AudioMediaSession.java,v $
+- * $Revision: 1.1 $
+- * $Date: 25/12/2006
+- * <p/>
+- * Copyright 2003-2006 Jive Software.
+- * <p/>
+- * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- * <p/>
+- * http://www.apache.org/licenses/LICENSE-2.0
+- * <p/>
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.jivesoftware.smackx.jingle.mediaimpl.jspeex;
+-
+-import java.io.IOException;
+-import java.net.DatagramSocket;
+-import java.net.InetAddress;
+-import java.net.ServerSocket;
+-import java.security.GeneralSecurityException;
+-
+-import javax.media.NoProcessorException;
+-import javax.media.format.UnsupportedFormatException;
+-import javax.media.rtp.rtcp.SenderReport;
+-import javax.media.rtp.rtcp.SourceDescription;
+-
+-import mil.jfcom.cie.media.session.MediaSession;
+-import mil.jfcom.cie.media.session.MediaSessionListener;
+-import mil.jfcom.cie.media.session.StreamPlayer;
+-import mil.jfcom.cie.media.srtp.packetizer.SpeexFormat;
+-
+-import org.jivesoftware.smackx.jingle.JingleSession;
+-import org.jivesoftware.smackx.jingle.SmackLogger;
+-import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
+-import org.jivesoftware.smackx.jingle.media.PayloadType;
+-import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
+-
+-/**
+- * This Class implements a complete JingleMediaSession.
+- * It sould be used to transmit and receive audio captured from the Mic.
+- * This Class should be automaticly controlled by JingleSession.
+- * But you could also use in any VOIP application.
+- * For better NAT Traversal support this implementation don't support only receive or only transmit.
+- * To receive you MUST transmit. So the only implemented and functionally methods are startTransmit() and stopTransmit()
+- *
+- * @author Thiago Camargo
+- */
+-
+-public class AudioMediaSession extends JingleMediaSession implements MediaSessionListener {
+-
+-	private static final SmackLogger LOGGER = SmackLogger.getLogger(AudioMediaSession.class);
+-
+-	private MediaSession mediaSession;
+-
+-    /**
+-     * Create a Session using Speex Codec
+-     *
+-     * @param localhost    localHost
+-     * @param localPort    localPort
+-     * @param remoteHost   remoteHost
+-     * @param remotePort   remotePort
+-     * @param eventHandler eventHandler
+-     * @param quality      quality
+-     * @param secure       secure
+-     * @param micOn        micOn
+-     * @return MediaSession
+-     * @throws NoProcessorException
+-     * @throws UnsupportedFormatException
+-     * @throws IOException
+-     * @throws GeneralSecurityException
+-     */
+-    public static MediaSession createSession(String localhost, int localPort, String remoteHost, int remotePort, MediaSessionListener eventHandler, int quality, boolean secure, boolean micOn) throws NoProcessorException, UnsupportedFormatException, IOException, GeneralSecurityException {
+-
+-        SpeexFormat.setFramesPerPacket(1);
+-        /**
+-         * The master key. Hardcoded for now.
+-         */
+-        byte[] masterKey = new byte[]{(byte) 0xE1, (byte) 0xF9, 0x7A, 0x0D, 0x3E, 0x01, (byte) 0x8B, (byte) 0xE0, (byte) 0xD6, 0x4F, (byte) 0xA3, 0x2C, 0x06, (byte) 0xDE, 0x41, 0x39};
+-
+-        /**
+-         * The master salt. Hardcoded for now.
+-         */
+-        byte[] masterSalt = new byte[]{0x0E, (byte) 0xC6, 0x75, (byte) 0xAD, 0x49, (byte) 0x8A, (byte) 0xFE, (byte) 0xEB, (byte) 0xB6, (byte) 0x96, 0x0B, 0x3A, (byte) 0xAB, (byte) 0xE6};
+-
+-        DatagramSocket[] localPorts = MediaSession.getLocalPorts(InetAddress.getByName(localhost), localPort);
+-        MediaSession session = MediaSession.createInstance(remoteHost, remotePort, localPorts, quality, secure, masterKey, masterSalt);
+-        session.setListener(eventHandler);
+-
+-        session.setSourceDescription(new SourceDescription[]{new SourceDescription(SourceDescription.SOURCE_DESC_NAME, "Superman", 1, false), new SourceDescription(SourceDescription.SOURCE_DESC_EMAIL, "cdcie.tester@je.jfcom.mil", 1, false), new SourceDescription(SourceDescription.SOURCE_DESC_LOC, InetAddress.getByName(localhost) + " Port " + session.getLocalDataPort(), 1, false), new SourceDescription(SourceDescription.SOURCE_DESC_TOOL, "JFCOM CDCIE Audio Chat", 1, false)});
+-        return session;
+-    }
+-
+-
+-    /**
+-     * Creates a org.jivesoftware.jingleaudio.jspeex.AudioMediaSession with defined payload type, remote and local candidates
+-     *
+-     * @param payloadType Payload of the jmf
+-     * @param remote      the remote information. The candidate that the jmf will be sent to.
+-     * @param local       the local information. The candidate that will receive the jmf
+-     * @param locator     media locator
+-     */
+-    public AudioMediaSession(final PayloadType payloadType, final TransportCandidate remote,
+-            final TransportCandidate local, String locator, JingleSession jingleSession) {
+-        super(payloadType, remote, local, locator == null ? "dsound://" : locator, jingleSession);
+-        initialize();
+-    }
+-
+-    /**
+-     * Initialize the Audio Channel to make it able to send and receive audio
+-     */
+-    public void initialize() {
+-
+-        String ip;
+-        String localIp;
+-        int localPort;
+-        int remotePort;
+-
+-        if (this.getLocal().getSymmetric() != null) {
+-            ip = this.getLocal().getIp();
+-            localIp = this.getLocal().getLocalIp();
+-            localPort = getFreePort();
+-            remotePort = this.getLocal().getSymmetric().getPort();
+-
+-            LOGGER.debug(this.getLocal().getConnection() + " " + ip + ": " + localPort + "->" + remotePort);
+-
+-        }
+-        else {
+-            ip = this.getRemote().getIp();
+-            localIp = this.getLocal().getLocalIp();
+-            localPort = this.getLocal().getPort();
+-            remotePort = this.getRemote().getPort();
+-        }
+-
+-        try {
+-            mediaSession = createSession(localIp, localPort, ip, remotePort, this, 2, false, true);
+-        }
+-        catch (NoProcessorException e) {
+-            e.printStackTrace();
+-        }
+-        catch (UnsupportedFormatException e) {
+-            e.printStackTrace();
+-        }
+-        catch (IOException e) {
+-            e.printStackTrace();
+-        }
+-        catch (GeneralSecurityException e) {
+-            e.printStackTrace();
+-        }
+-    }
+-
+-    /**
+-     * Starts transmission and for NAT Traversal reasons start receiving also.
+-     */
+-    public void startTrasmit() {
+-        try {
+-            LOGGER.debug("start");
+-            mediaSession.start(true);
+-            this.mediaReceived("");
+-        }
+-        catch (IOException e) {
+-            e.printStackTrace();
+-        }
+-    }
+-
+-    /**
+-     * Set transmit activity. If the active is true, the instance should trasmit.
+-     * If it is set to false, the instance should pause transmit.
+-     *
+-     * @param active active state
+-     */
+-    public void setTrasmit(boolean active) {
+-        // Do nothing
+-    }
+-
+-    /**
+-     * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
+-     */
+-    public void startReceive() {
+-        // Do nothing
+-    }
+-
+-    /**
+-     * Stops transmission and for NAT Traversal reasons stop receiving also.
+-     */
+-    public void stopTrasmit() {
+-        if (mediaSession != null)
+-            mediaSession.close();
+-    }
+-
+-    /**
+-     * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
+-     */
+-    public void stopReceive() {
+-        // Do nothing
+-    }
+-
+-    public void newStreamIdentified(StreamPlayer streamPlayer) {
+-    }
+-
+-    public void senderReportReceived(SenderReport report) {
+-    }
+-
+-    public void streamClosed(StreamPlayer stream, boolean timeout) {
+-    }
+-
+-    /**
+-     * Obtain a free port we can use.
+-     *
+-     * @return A free port number.
+-     */
+-    protected int getFreePort() {
+-        ServerSocket ss;
+-        int freePort = 0;
+-
+-        for (int i = 0; i < 10; i++) {
+-            freePort = (int) (10000 + Math.round(Math.random() * 10000));
+-            freePort = freePort % 2 == 0 ? freePort : freePort + 1;
+-            try {
+-                ss = new ServerSocket(freePort);
+-                freePort = ss.getLocalPort();
+-                ss.close();
+-                return freePort;
+-            }
+-            catch (IOException e) {
+-                e.printStackTrace();
+-            }
+-        }
+-        try {
+-            ss = new ServerSocket(0);
+-            freePort = ss.getLocalPort();
+-            ss.close();
+-        }
+-        catch (IOException e) {
+-            e.printStackTrace();
+-        }
+-        return freePort;
+-    }
+-}
Binary file doc/asmack-beem/lib/jstun.jar has changed
Binary file libs/asmack-android-7-beem.jar has changed
--- a/res/menu/contactlist_context.xml	Sat Dec 25 17:01:00 2010 +0100
+++ b/res/menu/contactlist_context.xml	Sun Dec 26 01:01:58 2010 +0100
@@ -5,7 +5,8 @@
 		</menu>
 	</item>
 	<item android:id="@+id/contact_list_context_menu_call_item"
-		android:title="@string/CDCall" android:visible="true" />
+		android:title="@string/CDCall" android:visible="true" 
+		><menu></menu></item>
 	<item android:id="@+id/contact_list_context_menu_user_info"
 		android:title="@string/CDInfos">
 		<menu>
--- a/src/com/beem/project/beem/jingle/JingleService.java	Sat Dec 25 17:01:00 2010 +0100
+++ b/src/com/beem/project/beem/jingle/JingleService.java	Sun Dec 26 01:01:58 2010 +0100
@@ -59,16 +59,15 @@
 import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
 
 import android.content.Context;
+import android.content.Intent;
 import android.net.Uri;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.util.Log;
-import android.content.Intent;
 
-import com.beem.project.beem.ui.Call;
-import com.beem.project.beem.jingle.RTPAudioSession;
 import com.beem.project.beem.service.aidl.IBeemJingleListener;
 import com.beem.project.beem.service.aidl.IJingle;
+import com.beem.project.beem.ui.Call;
 
 /**
  * Beem Jingle Service, manage jingle call.
@@ -131,6 +130,7 @@
      */
     @Override
     public void call(final String receiver) throws RemoteException {
+    	Log.d(TAG, "Place Call");
 	try {
 	    mOut = mJingleManager.createOutgoingJingleSession(receiver);
 	    mOut.addListener(new BeemJingleSessionListener());
@@ -144,6 +144,7 @@
 
     @Override
     public void acceptCall() throws RemoteException {
+    	Log.d(TAG, "Accept Call");
 	try {
 	    mRequest.accept();
 	    mIn.start();
@@ -199,7 +200,7 @@
 
 	@Override
 	public void sessionClosed(String reason, JingleSession jingleSession) {
-	    System.out.println("Session " + jingleSession.getResponder() + "closedd because " + reason);
+	    System.out.println("Session " + jingleSession.getResponder() + " closed because " + reason);
 
 	    final int n = mRemoteJingleListeners.beginBroadcast();
 	    for (int i = 0; i < n; i++) {
@@ -231,7 +232,7 @@
 
 	@Override
 	public void sessionDeclined(String reason, JingleSession jingleSession) {
-	    Log.d(TAG, "Session " + jingleSession.getResponder() + "declined because " + reason);
+	    Log.d(TAG, "Session " + jingleSession.getResponder() + " declined because " + reason);
 
 	    final int n = mRemoteJingleListeners.beginBroadcast();
 	    for (int i = 0; i < n; i++) {
@@ -249,7 +250,7 @@
 	@Override
 	public void sessionEstablished(PayloadType pt, TransportCandidate remoteCandidate,
 	    TransportCandidate localCandidate, JingleSession jingleSession) {
-	    Log.d(TAG, "Session " + jingleSession.getResponder() + "established");
+	    Log.d(TAG, "Session " + jingleSession.getResponder() + " established");
 	    mAudioSession = (RTPAudioSession) jingleSession.getSession().getMediaSession(MicrophoneRTPManager.MEDIA_NAME);
 	    final int n = mRemoteJingleListeners.beginBroadcast();
 	    for (int i = 0; i < n; i++) {
--- a/src/com/beem/project/beem/jingle/MicrophoneRTPManager.java	Sat Dec 25 17:01:00 2010 +0100
+++ b/src/com/beem/project/beem/jingle/MicrophoneRTPManager.java	Sun Dec 26 01:01:58 2010 +0100
@@ -54,6 +54,7 @@
 import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
 
 import android.content.Context;
+import android.provider.MediaStore.Audio;
 
 /**
  * RTPMediaManager, gere les payloads et renvoie une session RTP.
@@ -109,8 +110,8 @@
 	 */
 	private void setupPayloads() {
 		mPayloads = new ArrayList<PayloadType>();
-		mPayloads.add(new PayloadType.Audio(8, "PCMA"));
+		mPayloads.add(new PayloadType.Audio(8, "PCMA", 1, 8000));
 //		mPayloads.add(new PayloadType.Audio(9, "G722"));
-//		mPayloads.add(new PayloadType.Audio(3, "GSM"));
+//		mPayloads.add(new PayloadType.Audio(3, "GSM", 1, 8000));
 	}
 }
--- a/src/com/beem/project/beem/service/XmppFacade.java	Sat Dec 25 17:01:00 2010 +0100
+++ b/src/com/beem/project/beem/service/XmppFacade.java	Sun Dec 26 01:01:58 2010 +0100
@@ -156,14 +156,6 @@
     }
 
     /* (non-Javadoc)
-     * @see com.beem.project.beem.service.aidl.IXmppFacade#call(java.lang.String)
-     */
-    @Override
-    public void call(String jid) throws RemoteException {
-	mJingle.call(jid);
-    }
-
-    /* (non-Javadoc)
      * @see com.beem.project.beem.service.aidl.IXmppFacade#getVcardAvatar(java.lang.String)
      */
     @Override
--- a/src/com/beem/project/beem/ui/Call.java	Sat Dec 25 17:01:00 2010 +0100
+++ b/src/com/beem/project/beem/ui/Call.java	Sun Dec 26 01:01:58 2010 +0100
@@ -51,10 +51,10 @@
 import android.media.AudioManager;
 import android.media.Ringtone;
 import android.media.RingtoneManager;
-import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.os.Vibrator;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -66,13 +66,12 @@
 import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.TextView;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
 
 import com.beem.project.beem.R;
-import com.beem.project.beem.service.aidl.IXmppFacade;
+import com.beem.project.beem.service.Contact;
+import com.beem.project.beem.service.aidl.IBeemJingleListener;
 import com.beem.project.beem.service.aidl.IJingle;
-import com.beem.project.beem.service.aidl.IBeemJingleListener;
+import com.beem.project.beem.service.aidl.IXmppFacade;
 
 /**
  * This class is an activity which display an animation during the connection with the server.
@@ -94,6 +93,7 @@
     private Button mCloseCall;
     private Button mAcceptCall;
     private IJingle mJingle;
+    private Intent mIntent;
 
     final static long[] vibratePattern = {0,1000,1000};
 
@@ -136,13 +136,13 @@
 	super.onCreate(savedInstanceState);
 	Call.mContext = this;
 	setContentView(R.layout.call);
-	Intent callingIntent = this.getIntent();
+	mIntent = this.getIntent();
 
 	mLogo = (ImageView) findViewById(R.id.call_logo_anim);
 	mRotateAnim = AnimationUtils.loadAnimation(this, R.anim.rotate_and_scale);
 	mCloseCall = (Button) findViewById(R.id.call_cancel_button);
 	mCloseCall.setOnClickListener(new ClickListener());
-	if (callingIntent.getBooleanExtra("isCaller", false)) {
+	if (mIntent.getBooleanExtra("isCaller", false)) {
 	    mAcceptCall = (Button) findViewById(R.id.call_accept_button);
 	    mAcceptCall.setVisibility(View.GONE);
 	    call_state = UA_STATE_OUTGOING_CALL;
@@ -249,7 +249,7 @@
 	    } catch (RemoteException e) {
 		e.printStackTrace();
 	    }
-	    //finish();
+	    finish();
 	}
     }
 
@@ -324,6 +324,8 @@
      */
     private class BeemServiceConnection implements ServiceConnection {
 
+	private Contact mContact;
+
 	/**
 	 * Constructor.
 	 */
@@ -336,6 +338,8 @@
 	    try {
 		mJingle = mXmppFacade.getJingleService();
 		mJingle.addJingleListener(mJingleListener);
+		mContact = new Contact(getIntent().getData());
+		mJingle.call(mContact.getJIDWithRes());
 	    } catch (RemoteException e) {
 		e.printStackTrace();
 	    }
@@ -344,6 +348,7 @@
 	@Override
 	public void onServiceDisconnected(ComponentName name) {
 	    mXmppFacade = null;
+	    mJingle = null;
 	}
     }
 }
--- a/src/com/beem/project/beem/ui/ContactList.java	Sat Dec 25 17:01:00 2010 +0100
+++ b/src/com/beem/project/beem/ui/ContactList.java	Sun Dec 26 01:01:58 2010 +0100
@@ -39,9 +39,10 @@
 
     Flavien Astraud, November 26, 2009
 
-*/
+ */
 package com.beem.project.beem.ui;
 
+import java.io.ByteArrayInputStream;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -49,7 +50,6 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.io.ByteArrayInputStream;
 
 import org.jivesoftware.smack.util.StringUtils;
 
@@ -60,6 +60,8 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.SharedPreferences;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -75,8 +77,6 @@
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.widget.AdapterView;
-import android.widget.AdapterView.AdapterContextMenuInfo;
-import android.widget.AdapterView.OnItemClickListener;
 import android.widget.BaseAdapter;
 import android.widget.Filter;
 import android.widget.Filterable;
@@ -85,8 +85,8 @@
 import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.TextView;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.AdapterView.OnItemClickListener;
 
 import com.beem.project.beem.R;
 import com.beem.project.beem.service.Contact;
@@ -108,840 +108,842 @@
  */
 public class ContactList extends Activity {
 
-    private static final Intent SERVICE_INTENT = new Intent();
-    static {
-	SERVICE_INTENT.setComponent(new ComponentName("com.beem.project.beem", "com.beem.project.beem.BeemService"));
-    }
-
-    private static final String SETTINGS_HIDDEN_CONTACT = "settings_key_hidden_contact";
-    private static final String TAG = "ContactList";
-    private final BeemContactList mAdapterContactList = new BeemContactList();
-    private final List<String> mListGroup = new ArrayList<String>();
-
-    /** Map containing a list of the different contacts of a given group.
-     * Each list is a @{link SortedList} so there is no need to sort it again.
-     * */
-    private final Map<String, List<Contact>> mContactOnGroup = new HashMap<String, List<Contact>>();
-    private final BeemContactListOnClick mOnContactClick = new BeemContactListOnClick();
-    private final Handler mHandler = new Handler();
-    private final ServiceConnection mServConn = new BeemServiceConnection();
-    private final BeemBroadcastReceiver mReceiver = new BeemBroadcastReceiver();
-    private final ComparatorContactListByStatusAndName<Contact> mComparator =
-	new ComparatorContactListByStatusAndName<Contact>();
-    private final BeemRosterListener mBeemRosterListener = new BeemRosterListener();
-    private List<Contact> mListContact;
-    private String mSelectedGroup;
-    private IRoster mRoster;
-    private Contact mSelectedContact;
-    private IXmppFacade mXmppFacade;
-    private IChatManager mChatManager;
-    private SharedPreferences mSettings;
-    private LayoutInflater mInflater;
-    private BeemBanner mAdapterBanner;
-    private boolean mBinded;
-
-    /**
-     * Constructor.
-     */
-    public ContactList() {
-    }
-
-    /**
-     * Callback for menu creation.
-     * @param menu the menu created
-     * @return true on success, false otherwise
-     */
-    @Override
-    public final boolean onCreateOptionsMenu(Menu menu) {
-	super.onCreateOptionsMenu(menu);
-	MenuInflater inflater = getMenuInflater();
-	inflater.inflate(R.menu.contact_list, menu);
-	return true;
-    }
-
-    @Override
-    public final boolean onOptionsItemSelected(MenuItem item) {
-	switch (item.getItemId()) {
-	    case R.id.contact_list_menu_settings:
-		startActivity(new Intent(this, Settings.class));
-		return true;
-	    case R.id.contact_list_menu_add_contact:
-		startActivity(new Intent(ContactList.this, AddContact.class));
-		return true;
-	    case R.id.menu_change_status:
-		startActivity(new Intent(ContactList.this, ChangeStatus.class));
-		return true;
-	    case R.id.contact_list_menu_chatlist:
-		List<Contact> openedChats;
-		try {
-		    openedChats = mChatManager.getOpenedChatList();
-		    Log.d(TAG, "opened chats = " + openedChats);
-		    Dialog chatList = new ChatList(ContactList.this, openedChats).create();
-		    chatList.show();
-		} catch (RemoteException e) {
-		    e.printStackTrace();
-		}
-		return true;
-	    case R.id.menu_disconnect:
-		stopService(SERVICE_INTENT);
-		finish();
-	    default:
-		return false;
-	}
-    }
-
-    @Override
-    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
-	super.onCreateContextMenu(menu, v, menuInfo);
-	MenuInflater inflater = getMenuInflater();
-	inflater.inflate(R.menu.contactlist_context, menu);
-	AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
-	Contact c = mListContact.get(info.position);
-	try {
-	    mSelectedContact = mRoster.getContact(c.getJID());
-	} catch (RemoteException e) {
-	    e.printStackTrace();
-	}
-	menu.setHeaderTitle(mSelectedContact.getJID());
-    }
-
-    @Override
-    public boolean onContextItemSelected(MenuItem item) {
-	Intent in;
-	boolean result;
-	if (mSelectedContact != null) {
-	    switch (item.getItemId()) {
-		case R.id.contact_list_context_menu_chat_item:
-		    List<String> res = mSelectedContact.getMRes();
-		    if (res.isEmpty()) {
-			result = false;
-			break;
-		    }
-		    for (String resv : res) {
-			in = new Intent(this, Chat.class);
-			in.setData(mSelectedContact.toUri(resv));
-			item.getSubMenu().add(resv).setIntent(in);
-		    }
-		    result = true;
-		    break;
-		case R.id.contact_list_context_menu_call_item:
-		    try {
-			mJingle.call(mContact.getJID() + "/Beem");
-			in = new Intent(this, Call.class);
-			in.setData(mContact.toUri());
-			in.putExtra("isCaller", true);
-			startActivity(in);
-			result = true;
-		    } catch (RemoteException e) {
-			e.printStackTrace();
-		    }
-		    result = true;
-		    break;
-		case R.id.contact_list_context_menu_user_info:
-		    item.getSubMenu().setHeaderTitle(mSelectedContact.getJID());
-		    result = true;
-		    break;
-		case R.id.contact_list_context_menu_userinfo_alias:
-		    Dialog alias = new Alias(ContactList.this, mRoster, mSelectedContact).create();
-		    alias.show();
-		    result = true;
-		    break;
-		case R.id.contact_list_context_menu_userinfo_group:
-		    in = new Intent(this, GroupList.class);
-		    in.putExtra("contact", mSelectedContact);
-		    startActivity(in);
-		    result = true;
-		    break;
-		case R.id.contact_list_context_menu_userinfo_subscription:
-		    Dialog subscription = new ResendSubscription(ContactList.this,
-			mXmppFacade, mSelectedContact).create();
-		    subscription.show();
-		    result = true;
-		    break;
-		case R.id.contact_list_context_menu_userinfo_block:
-		    result = true;
-		    break;
-		case R.id.contact_list_context_menu_userinfo_delete:
-		    Dialog delete = new DeleteContact(ContactList.this, mRoster, mSelectedContact).create();
-		    delete.show();
-		    result = true;
-		    break;
-		default:
-		    result = super.onContextItemSelected(item);
-		    break;
-	    }
-	    return result;
-	}
-	return super.onContextItemSelected(item);
-    }
-
-    @Override
-    protected void onCreate(Bundle saveBundle) {
-	super.onCreate(saveBundle);
-	mSettings = PreferenceManager.getDefaultSharedPreferences(this);
-	setContentView(R.layout.contactlist);
-
-	this.registerReceiver(mReceiver, new IntentFilter(BeemBroadcastReceiver.BEEM_CONNECTION_CLOSED));
-
-	mInflater = getLayoutInflater();
-	mAdapterBanner = new BeemBanner(mInflater, mListGroup);
-	mListContact = new ArrayList<Contact>();
-	ListView listView = (ListView) findViewById(R.id.contactlist);
-	listView.setOnItemClickListener(mOnContactClick);
-	registerForContextMenu(listView);
-	listView.setAdapter(mAdapterContactList);
-    }
-
-    @Override
-    protected void onResume() {
-	super.onResume();
-	if (!mBinded)
-	    mBinded = bindService(SERVICE_INTENT, mServConn, BIND_AUTO_CREATE);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected void onPause() {
-	super.onPause();
-	try {
-	    if (mRoster != null) {
-		mRoster.removeRosterListener(mBeemRosterListener);
-		mRoster = null;
-	    }
-	} catch (RemoteException e) {
-	    Log.d("ContactList", "Remote exception", e);
-	}
-	if (mBinded) {
-	    unbindService(mServConn);
-	    mBinded = false;
-	}
-	mXmppFacade = null;
-    }
-
-    @Override
-    protected void onDestroy() {
-	super.onDestroy();
-	this.unregisterReceiver(mReceiver);
-	Log.e(TAG, "onDestroy activity");
-    }
-
-    /**
-     * Build and display the contact list.
-     * @param group name of the contact list.
-     */
-    private void buildContactList(String group) {
-	mListContact = mContactOnGroup.get(group);
-	mSelectedGroup = group;
-	Log.d(TAG, "buildContactList for group " + group);
-	mAdapterContactList.notifyDataSetChanged();
-    }
-
-    /**
-     * Show the groups view.
-     */
-    private void showGroups() {
-
-	ViewStub stub = (ViewStub) findViewById(R.id.contactlist_stub);
-	if (stub != null) {
-	    View v = stub.inflate();
-	    Gallery g = (Gallery) v.findViewById(R.id.contactlist_banner);
-	    g.setOnItemClickListener(new OnItemClickGroupName());
-	    g.setAdapter(mAdapterBanner);
-	    g.setSelection(0);
-	} else {
-	    ((LinearLayout) findViewById(R.id.contactlist_groupstub)).setVisibility(View.VISIBLE);
-	    Gallery g = (Gallery) findViewById(R.id.contactlist_banner);
-	    g.setSelection(0);
-	}
-    }
-
-    /**
-     * Hide the groups view.
-     */
-    private void hideGroups() {
-	View v = findViewById(R.id.contactlist_groupstub);
-	if (v != null)
-	    v.setVisibility(View.GONE);
-    }
-
-    /**
-     * Listener on service event.
-     */
-    private class BeemRosterListener extends IBeemRosterListener.Stub {
-	/**
-	 * Constructor.
-	 */
-	public BeemRosterListener() {
+	private static final Intent SERVICE_INTENT = new Intent();
+	static {
+		SERVICE_INTENT.setComponent(new ComponentName("com.beem.project.beem", "com.beem.project.beem.BeemService"));
 	}
 
-	/**
-	 * {@inheritDoc}
-	 * Simple stategy to handle the onEntriesAdded event.
-	 * if contact has to be shown :
-	 * <ul>
-	 * <li> add him to his groups</li>
-	 * <li> add him to the specials groups</>
-	 * </ul>
-	 */
-	@Override
-	public void onEntriesAdded(final List<String> addresses) throws RemoteException {
-	    final boolean hideDisconnected = mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false);
-	    for (String newName : addresses) {
-		Contact contact = mRoster.getContact(newName);
-		boolean visible = !hideDisconnected || Status.statusOnline(contact.getStatus());
-		List<String> groups = contact.getGroups();
-		if (visible) {
-		    for (String group : groups) {
-			if (!mListGroup.contains(group)) {
-			    mListGroup.add(mListGroup.size() - 1, group);
-			    List<Contact> tmplist = new SortedList<Contact>(new LinkedList<Contact>(), mComparator);
-			    mContactOnGroup.put(group, tmplist);
-			}
-			List<Contact> contactByGroups = mContactOnGroup.get(group);
-			if (mSelectedGroup.equals(group)) {
-			    updateCurrentList(group, contact);
-			    continue;
-			}
-			contactByGroups.add(contact);
-		    }
-
-		    // add the contact to all and no groups
-		    addToSpecialList(contact);
-		}
-	    }
-	}
-
-	/**
-	 * {@inheritDoc}
-	 * Simple stategy to handle the onEntriesDeleted event.
-	 * <ul>
-	 * <li> Remove the contact from all groups</li>
-	 * </ul>
-	 */
-	@Override
-	public void onEntriesDeleted(final List<String> addresses) throws RemoteException {
-	    Log.d(TAG, "onEntries deleted " + addresses);
-	    for (String cToDelete : addresses) {
-		Contact contact = new Contact(cToDelete);
-		for (Map.Entry<String, List<Contact>> entry : mContactOnGroup.entrySet()) {
-		    List<Contact> contactByGroups = entry.getValue();
-		    if (mSelectedGroup.equals(entry.getKey())) {
-			updateCurrentList(entry.getKey(), contact);
-			continue;
-		    }
-		    contactByGroups.remove(contact);
-		}
-		cleanBannerGroup();
-	    }
-
-	    mHandler.post(new Runnable() {
-		public void run() {
-		    mSelectedGroup = getString(R.string.contact_list_all_contact);
-		    mListContact = mContactOnGroup.get(mSelectedGroup);
-
-		    mAdapterContactList.notifyDataSetChanged();
-		}
-	    });
-
-	}
-
-	/**
-	 * {@inheritDoc}
-	 * Simple stategy to handle the onEntriesUpdated event.
-	 * <ul>
-	 * <li> Remove the contact from all groups</li>
-	 * <li> if contact has to be shown add it to his groups</li>
-	 * <li> if contact has to be shown add it to the specials groups</li>
-	 * </ul>
-	 */
-	@Override
-	public void onEntriesUpdated(final List<String> addresses) throws RemoteException {
-	    final boolean hideDisconnected = mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false);
-	    for (String adr : addresses) {
-		Contact contact = mRoster.getContact(adr);
-		boolean visible = !hideDisconnected || Status.statusOnline(contact.getStatus());
-		List<String> groups = contact.getGroups();
-		for (Map.Entry<String, List<Contact>> entry : mContactOnGroup.entrySet()) {
-		    List<Contact> contactByGroups = entry.getValue();
-		    if (mSelectedGroup.equals(entry.getKey())) {
-			updateCurrentList(entry.getKey(), contact);
-			continue;
-		    }
-		    contactByGroups.remove(contact);
-		    if (visible) {
-			for (String group : groups) {
-			    if (!mListGroup.contains(group)) {
-				mListGroup.add(mListGroup.size() - 1, group);
-				List<Contact> tmplist = new SortedList<Contact>(
-				    new LinkedList<Contact>(), mComparator);
-				mContactOnGroup.put(group, tmplist);
-			    }
-			    mContactOnGroup.get(group).remove(contact);
-			}
-		    }
-
-		}
+	private static final String SETTINGS_HIDDEN_CONTACT = "settings_key_hidden_contact";
+	private static final String TAG = "ContactList";
+	private final BeemContactList mAdapterContactList = new BeemContactList();
+	private final List<String> mListGroup = new ArrayList<String>();
 
-		// add the contact to all and no groups
-		if (visible) {
-		    addToSpecialList(contact);
-		}
-	    }
-	    cleanBannerGroup();
-	}
-
-	/**
-	 * {@inheritDoc}
-	 * Simple stategy to handle the onPresenceChanged event.
-	 * <ul>
-	 * <li> Remove the contact from all groups</li>
-	 * <li> if contact has to be shown add it to his groups</li>
-	 * <li> if contact has to be shown add it to the specials groups</li>
-	 * </ul>
-	 */
-	@Override
-	public void onPresenceChanged(PresenceAdapter presence) throws RemoteException {
-	    Log.d(TAG, "presence");
-	    String from = presence.getFrom();
-	    final boolean hideDisconnected = mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false);
-	    final Contact contact = mRoster.getContact(StringUtils.parseBareAddress(from));
-	    boolean visible = !hideDisconnected || Status.statusOnline(contact.getStatus());
-	    List<String> groups = contact.getGroups();
-	    for (Map.Entry<String, List<Contact>> entry : mContactOnGroup.entrySet()) {
-		List<Contact> contactByGroups = entry.getValue();
-		if (mSelectedGroup.equals(entry.getKey())) {
-		    updateCurrentList(entry.getKey(), contact);
-		    continue;
-		}
-		contactByGroups.remove(contact);
-		if (visible) {
-		    if (groups.contains(entry.getKey())) {
-			contactByGroups.add(contact);
-		    }
-		}
-	    }
-	    if (visible) {
-		addToSpecialList(contact);
-	    }
-	}
-
-	/**
-	 * Add a contact to the special list No Group and All contacts.
-	 * The contact will be added if the list is not the current list otherwise
-	 * the list must be modified in a Handler.
-	 *
-	 * @param contact the contact to add.
-	 */
-	private void addToSpecialList(Contact contact) {
-	    List<String> groups = contact.getGroups();
-	    List<Contact> list = mContactOnGroup.get(getString(R.string.contact_list_all_contact));
-	    if (list != mListContact) {
-		list.add(contact);
-	    }
-	    list = mContactOnGroup.get(getString(R.string.contact_list_no_group));
-	    if (list != mListContact && groups.isEmpty()) {
-		list.add(contact);
-	    }
-	}
-
-	/**
-	 * Update the current list with the status of contact.
-	 *
-	 * @param listName name of the current list
-	 * @param contact contact to update
-	 */
-	private void updateCurrentList(String listName, final Contact contact) {
-	    final boolean hideDisconnected = mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false);
-	    final List<String> groups = contact.getGroups();
-	    String noGroup = getString(R.string.contact_list_no_group);
-	    String allGroup = getString(R.string.contact_list_all_contact);
-	    final boolean add = ((!hideDisconnected || Status.statusOnline(contact.getStatus())) &&	// must show and
-		(
-		    (listName.equals(noGroup) && groups.isEmpty()) ||			// in no group
-		    groups.contains(listName) ||					// or in current
-		    listName.equals(allGroup)						// or in all
-		));
-	    mHandler.post(new Runnable() {
-		public void run() {
-		    mListContact.remove(contact);
-		    if (add) {
-			mListContact.add(contact);
-		    }
-		    mAdapterContactList.notifyDataSetChanged();
-		}
-	    });
-
-	}
-
-	/**
-	 * Remove old groups on the banner.
-	 * @throws RemoteException if an error occur when communicating with the service
-	 */
-	private void cleanBannerGroup() throws RemoteException {
-	    List<String> rosterGroups = mRoster.getGroupsNames();
-	    List<String> realGroups = mListGroup.subList(1, mListContact.size() - 1);
-	    realGroups.retainAll(rosterGroups);
-	}
-
-    }
-
-    /**
-     * Adapter contact list.
-     */
-    private class BeemContactList extends BaseAdapter implements Filterable {
-
-	private final ContactFilter mFilter;
+	/** Map containing a list of the different contacts of a given group.
+	 * Each list is a @{link SortedList} so there is no need to sort it again.
+	 * */
+	private final Map<String, List<Contact>> mContactOnGroup = new HashMap<String, List<Contact>>();
+	private final BeemContactListOnClick mOnContactClick = new BeemContactListOnClick();
+	private final Handler mHandler = new Handler();
+	private final ServiceConnection mServConn = new BeemServiceConnection();
+	private final BeemBroadcastReceiver mReceiver = new BeemBroadcastReceiver();
+	private final ComparatorContactListByStatusAndName<Contact> mComparator =
+		new ComparatorContactListByStatusAndName<Contact>();
+	private final BeemRosterListener mBeemRosterListener = new BeemRosterListener();
+	private List<Contact> mListContact;
+	private String mSelectedGroup;
+	private IRoster mRoster;
+	private Contact mSelectedContact;
+	private IXmppFacade mXmppFacade;
+	private IChatManager mChatManager;
+	private SharedPreferences mSettings;
+	private LayoutInflater mInflater;
+	private BeemBanner mAdapterBanner;
+	private boolean mBinded;
 
 	/**
 	 * Constructor.
 	 */
-	public BeemContactList() {
-	    mFilter = new ContactFilter();
-	}
+	 public ContactList() {
+	 }
+
+	 /**
+	  * Callback for menu creation.
+	  * @param menu the menu created
+	  * @return true on success, false otherwise
+	  */
+	 @Override
+	 public final boolean onCreateOptionsMenu(Menu menu) {
+		 super.onCreateOptionsMenu(menu);
+		 MenuInflater inflater = getMenuInflater();
+		 inflater.inflate(R.menu.contact_list, menu);
+		 return true;
+	 }
+
+	 @Override
+	 public final boolean onOptionsItemSelected(MenuItem item) {
+		 switch (item.getItemId()) {
+		 case R.id.contact_list_menu_settings:
+			 startActivity(new Intent(this, Settings.class));
+			 return true;
+		 case R.id.contact_list_menu_add_contact:
+			 startActivity(new Intent(ContactList.this, AddContact.class));
+			 return true;
+		 case R.id.menu_change_status:
+			 startActivity(new Intent(ContactList.this, ChangeStatus.class));
+			 return true;
+		 case R.id.contact_list_menu_chatlist:
+			 List<Contact> openedChats;
+			 try {
+				 openedChats = mChatManager.getOpenedChatList();
+				 Log.d(TAG, "opened chats = " + openedChats);
+				 Dialog chatList = new ChatList(ContactList.this, openedChats).create();
+				 chatList.show();
+			 } catch (RemoteException e) {
+				 e.printStackTrace();
+			 }
+			 return true;
+		 case R.id.menu_disconnect:
+			 stopService(SERVICE_INTENT);
+			 finish();
+		 default:
+			 return false;
+		 }
+	 }
+
+	 @Override
+	 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+		 super.onCreateContextMenu(menu, v, menuInfo);
+		 MenuInflater inflater = getMenuInflater();
+		 inflater.inflate(R.menu.contactlist_context, menu);
+		 AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
+		 Contact c = mListContact.get(info.position);
+		 try {
+			 mSelectedContact = mRoster.getContact(c.getJID());
+		 } catch (RemoteException e) {
+			 e.printStackTrace();
+		 }
+		 menu.setHeaderTitle(mSelectedContact.getJID());
+	 }
 
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public int getCount() {
-	    return mListContact.size();
-	}
+	 @Override
+	 public boolean onContextItemSelected(MenuItem item) {
+		 List<String> res;
+		 Intent in;
+		 boolean result;
+		 if (mSelectedContact != null) {
+			 switch (item.getItemId()) {
+			 case R.id.contact_list_context_menu_chat_item:
+				 res = mSelectedContact.getMRes();
+				 if (res.isEmpty()) {
+					 result = false;
+					 break;
+				 }
+				 for (String resv : res) {
+					 in = new Intent(this, Chat.class);
+					 in.setData(mSelectedContact.toUri(resv));
+					 item.getSubMenu().add(resv).setIntent(in);
+				 }
+				 result = true;
+				 break;
+			 case R.id.contact_list_context_menu_call_item:
+				 res = mSelectedContact.getMRes();
+				 if (res.isEmpty()) {
+					 result = false;
+					 break;
+				 }
+				 for (String resv : res) {
+					 in = new Intent(this, Call.class);
+					 in.setData(mSelectedContact.toUri(resv));
+					 in.putExtra("isCaller", true);
+					 item.getSubMenu().add(resv).setIntent(in);
+				 }
+				 result = true;
+				 break;
+			 case R.id.contact_list_context_menu_user_info:
+				 item.getSubMenu().setHeaderTitle(mSelectedContact.getJID());
+				 result = true;
+				 break;
+			 case R.id.contact_list_context_menu_userinfo_alias:
+				 Dialog alias = new Alias(ContactList.this, mRoster, mSelectedContact).create();
+				 alias.show();
+				 result = true;
+				 break;
+			 case R.id.contact_list_context_menu_userinfo_group:
+				 in = new Intent(this, GroupList.class);
+				 in.putExtra("contact", mSelectedContact);
+				 startActivity(in);
+				 result = true;
+				 break;
+			 case R.id.contact_list_context_menu_userinfo_subscription:
+				 Dialog subscription = new ResendSubscription(ContactList.this,
+						 mXmppFacade, mSelectedContact).create();
+				 subscription.show();
+				 result = true;
+				 break;
+			 case R.id.contact_list_context_menu_userinfo_block:
+				 result = true;
+				 break;
+			 case R.id.contact_list_context_menu_userinfo_delete:
+				 Dialog delete = new DeleteContact(ContactList.this, mRoster, mSelectedContact).create();
+				 delete.show();
+				 result = true;
+				 break;
+			 default:
+				 result = super.onContextItemSelected(item);
+				 break;
+			 }
+			 return result;
+		 }
+		 return super.onContextItemSelected(item);
+	 }
+
+	 @Override
+	 protected void onCreate(Bundle saveBundle) {
+		 super.onCreate(saveBundle);
+		 mSettings = PreferenceManager.getDefaultSharedPreferences(this);
+		 setContentView(R.layout.contactlist);
+
+		 this.registerReceiver(mReceiver, new IntentFilter(BeemBroadcastReceiver.BEEM_CONNECTION_CLOSED));
+
+		 mInflater = getLayoutInflater();
+		 mAdapterBanner = new BeemBanner(mInflater, mListGroup);
+		 mListContact = new ArrayList<Contact>();
+		 ListView listView = (ListView) findViewById(R.id.contactlist);
+		 listView.setOnItemClickListener(mOnContactClick);
+		 registerForContextMenu(listView);
+		 listView.setAdapter(mAdapterContactList);
+	 }
+
+	 @Override
+	 protected void onResume() {
+		 super.onResume();
+		 if (!mBinded)
+			 mBinded = bindService(SERVICE_INTENT, mServConn, BIND_AUTO_CREATE);
+	 }
+
+	 /**
+	  * {@inheritDoc}
+	  */
+	 @Override
+	 protected void onPause() {
+		 super.onPause();
+		 try {
+			 if (mRoster != null) {
+				 mRoster.removeRosterListener(mBeemRosterListener);
+				 mRoster = null;
+			 }
+		 } catch (RemoteException e) {
+			 Log.d("ContactList", "Remote exception", e);
+		 }
+		 if (mBinded) {
+			 unbindService(mServConn);
+			 mBinded = false;
+		 }
+		 mXmppFacade = null;
+	 }
+
+	 @Override
+	 protected void onDestroy() {
+		 super.onDestroy();
+		 this.unregisterReceiver(mReceiver);
+		 Log.e(TAG, "onDestroy activity");
+	 }
+
+	 /**
+	  * Build and display the contact list.
+	  * @param group name of the contact list.
+	  */
+	 private void buildContactList(String group) {
+		 mListContact = mContactOnGroup.get(group);
+		 mSelectedGroup = group;
+		 Log.d(TAG, "buildContactList for group " + group);
+		 mAdapterContactList.notifyDataSetChanged();
+	 }
+
+	 /**
+	  * Show the groups view.
+	  */
+	 private void showGroups() {
+
+		 ViewStub stub = (ViewStub) findViewById(R.id.contactlist_stub);
+		 if (stub != null) {
+			 View v = stub.inflate();
+			 Gallery g = (Gallery) v.findViewById(R.id.contactlist_banner);
+			 g.setOnItemClickListener(new OnItemClickGroupName());
+			 g.setAdapter(mAdapterBanner);
+			 g.setSelection(0);
+		 } else {
+			 ((LinearLayout) findViewById(R.id.contactlist_groupstub)).setVisibility(View.VISIBLE);
+			 Gallery g = (Gallery) findViewById(R.id.contactlist_banner);
+			 g.setSelection(0);
+		 }
+	 }
+
+	 /**
+	  * Hide the groups view.
+	  */
+	 private void hideGroups() {
+		 View v = findViewById(R.id.contactlist_groupstub);
+		 if (v != null)
+			 v.setVisibility(View.GONE);
+	 }
+
+	 /**
+	  * Listener on service event.
+	  */
+	 private class BeemRosterListener extends IBeemRosterListener.Stub {
+		 /**
+		  * Constructor.
+		  */
+		 public BeemRosterListener() {
+		 }
 
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public Object getItem(int position) {
-	    return mListContact.get(position);
-	}
+		 /**
+		  * {@inheritDoc}
+		  * Simple stategy to handle the onEntriesAdded event.
+		  * if contact has to be shown :
+		  * <ul>
+		  * <li> add him to his groups</li>
+		  * <li> add him to the specials groups</>
+		  * </ul>
+		  */
+		 @Override
+		 public void onEntriesAdded(final List<String> addresses) throws RemoteException {
+			 final boolean hideDisconnected = mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false);
+			 for (String newName : addresses) {
+				 Contact contact = mRoster.getContact(newName);
+				 boolean visible = !hideDisconnected || Status.statusOnline(contact.getStatus());
+				 List<String> groups = contact.getGroups();
+				 if (visible) {
+					 for (String group : groups) {
+						 if (!mListGroup.contains(group)) {
+							 mListGroup.add(mListGroup.size() - 1, group);
+							 List<Contact> tmplist = new SortedList<Contact>(new LinkedList<Contact>(), mComparator);
+							 mContactOnGroup.put(group, tmplist);
+						 }
+						 List<Contact> contactByGroups = mContactOnGroup.get(group);
+						 if (mSelectedGroup.equals(group)) {
+							 updateCurrentList(group, contact);
+							 continue;
+						 }
+						 contactByGroups.add(contact);
+					 }
+
+					 // add the contact to all and no groups
+					 addToSpecialList(contact);
+				 }
+			 }
+		 }
+
+		 /**
+		  * {@inheritDoc}
+		  * Simple stategy to handle the onEntriesDeleted event.
+		  * <ul>
+		  * <li> Remove the contact from all groups</li>
+		  * </ul>
+		  */
+		 @Override
+		 public void onEntriesDeleted(final List<String> addresses) throws RemoteException {
+			 Log.d(TAG, "onEntries deleted " + addresses);
+			 for (String cToDelete : addresses) {
+				 Contact contact = new Contact(cToDelete);
+				 for (Map.Entry<String, List<Contact>> entry : mContactOnGroup.entrySet()) {
+					 List<Contact> contactByGroups = entry.getValue();
+					 if (mSelectedGroup.equals(entry.getKey())) {
+						 updateCurrentList(entry.getKey(), contact);
+						 continue;
+					 }
+					 contactByGroups.remove(contact);
+				 }
+				 cleanBannerGroup();
+			 }
+
+			 mHandler.post(new Runnable() {
+				 public void run() {
+					 mSelectedGroup = getString(R.string.contact_list_all_contact);
+					 mListContact = mContactOnGroup.get(mSelectedGroup);
+
+					 mAdapterContactList.notifyDataSetChanged();
+				 }
+			 });
+
+		 }
+
+		 /**
+		  * {@inheritDoc}
+		  * Simple stategy to handle the onEntriesUpdated event.
+		  * <ul>
+		  * <li> Remove the contact from all groups</li>
+		  * <li> if contact has to be shown add it to his groups</li>
+		  * <li> if contact has to be shown add it to the specials groups</li>
+		  * </ul>
+		  */
+		 @Override
+		 public void onEntriesUpdated(final List<String> addresses) throws RemoteException {
+			 final boolean hideDisconnected = mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false);
+			 for (String adr : addresses) {
+				 Contact contact = mRoster.getContact(adr);
+				 boolean visible = !hideDisconnected || Status.statusOnline(contact.getStatus());
+				 List<String> groups = contact.getGroups();
+				 for (Map.Entry<String, List<Contact>> entry : mContactOnGroup.entrySet()) {
+					 List<Contact> contactByGroups = entry.getValue();
+					 if (mSelectedGroup.equals(entry.getKey())) {
+						 updateCurrentList(entry.getKey(), contact);
+						 continue;
+					 }
+					 contactByGroups.remove(contact);
+					 if (visible) {
+						 for (String group : groups) {
+							 if (!mListGroup.contains(group)) {
+								 mListGroup.add(mListGroup.size() - 1, group);
+								 List<Contact> tmplist = new SortedList<Contact>(
+										 new LinkedList<Contact>(), mComparator);
+								 mContactOnGroup.put(group, tmplist);
+							 }
+							 mContactOnGroup.get(group).remove(contact);
+						 }
+					 }
+
+				 }
+
+				 // add the contact to all and no groups
+				 if (visible) {
+					 addToSpecialList(contact);
+				 }
+			 }
+			 cleanBannerGroup();
+		 }
 
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public long getItemId(int position) {
-	    return mListContact.get(position).hashCode();
-	}
+		 /**
+		  * {@inheritDoc}
+		  * Simple stategy to handle the onPresenceChanged event.
+		  * <ul>
+		  * <li> Remove the contact from all groups</li>
+		  * <li> if contact has to be shown add it to his groups</li>
+		  * <li> if contact has to be shown add it to the specials groups</li>
+		  * </ul>
+		  */
+		 @Override
+		 public void onPresenceChanged(PresenceAdapter presence) throws RemoteException {
+			 Log.d(TAG, "presence");
+			 String from = presence.getFrom();
+			 final boolean hideDisconnected = mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false);
+			 final Contact contact = mRoster.getContact(StringUtils.parseBareAddress(from));
+			 boolean visible = !hideDisconnected || Status.statusOnline(contact.getStatus());
+			 List<String> groups = contact.getGroups();
+			 for (Map.Entry<String, List<Contact>> entry : mContactOnGroup.entrySet()) {
+				 List<Contact> contactByGroups = entry.getValue();
+				 if (mSelectedGroup.equals(entry.getKey())) {
+					 updateCurrentList(entry.getKey(), contact);
+					 continue;
+				 }
+				 contactByGroups.remove(contact);
+				 if (visible) {
+					 if (groups.contains(entry.getKey())) {
+						 contactByGroups.add(contact);
+					 }
+				 }
+			 }
+			 if (visible) {
+				 addToSpecialList(contact);
+			 }
+		 }
+
+		 /**
+		  * Add a contact to the special list No Group and All contacts.
+		  * The contact will be added if the list is not the current list otherwise
+		  * the list must be modified in a Handler.
+		  *
+		  * @param contact the contact to add.
+		  */
+		 private void addToSpecialList(Contact contact) {
+			 List<String> groups = contact.getGroups();
+			 List<Contact> list = mContactOnGroup.get(getString(R.string.contact_list_all_contact));
+			 if (list != mListContact) {
+				 list.add(contact);
+			 }
+			 list = mContactOnGroup.get(getString(R.string.contact_list_no_group));
+			 if (list != mListContact && groups.isEmpty()) {
+				 list.add(contact);
+			 }
+		 }
+
+		 /**
+		  * Update the current list with the status of contact.
+		  *
+		  * @param listName name of the current list
+		  * @param contact contact to update
+		  */
+		 private void updateCurrentList(String listName, final Contact contact) {
+			 final boolean hideDisconnected = mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false);
+			 final List<String> groups = contact.getGroups();
+			 String noGroup = getString(R.string.contact_list_no_group);
+			 String allGroup = getString(R.string.contact_list_all_contact);
+			 final boolean add = ((!hideDisconnected || Status.statusOnline(contact.getStatus())) &&	// must show and
+					 (
+							 (listName.equals(noGroup) && groups.isEmpty()) ||			// in no group
+							 groups.contains(listName) ||					// or in current
+							 listName.equals(allGroup)						// or in all
+					 ));
+			 mHandler.post(new Runnable() {
+				 public void run() {
+					 mListContact.remove(contact);
+					 if (add) {
+						 mListContact.add(contact);
+					 }
+					 mAdapterContactList.notifyDataSetChanged();
+				 }
+			 });
+
+		 }
+
+		 /**
+		  * Remove old groups on the banner.
+		  * @throws RemoteException if an error occur when communicating with the service
+		  */
+		 private void cleanBannerGroup() throws RemoteException {
+			 List<String> rosterGroups = mRoster.getGroupsNames();
+			 List<String> realGroups = mListGroup.subList(1, mListContact.size() - 1);
+			 realGroups.retainAll(rosterGroups);
+		 }
+
+	 }
+
+	 /**
+	  * Adapter contact list.
+	  */
+	 private class BeemContactList extends BaseAdapter implements Filterable {
+
+		 private final ContactFilter mFilter;
+
+		 /**
+		  * Constructor.
+		  */
+		 public BeemContactList() {
+			 mFilter = new ContactFilter();
+		 }
+
+		 /**
+		  * {@inheritDoc}
+		  */
+		 @Override
+		 public int getCount() {
+			 return mListContact.size();
+		 }
+
+		 /**
+		  * {@inheritDoc}
+		  */
+		 @Override
+		 public Object getItem(int position) {
+			 return mListContact.get(position);
+		 }
+
+		 /**
+		  * {@inheritDoc}
+		  */
+		 @Override
+		 public long getItemId(int position) {
+			 return mListContact.get(position).hashCode();
+		 }
 
 
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public View getView(int position, View convertView, ViewGroup parent) {
-	    View v = convertView;
-	    if (convertView == null) {
-		v = mInflater.inflate(R.layout.contactlistcontact, null);
-	    }
-	    Contact c = mListContact.get(position);
-	    if (mRoster != null) {
-		try {
-		    c = mRoster.getContact(c.getJID());
-		} catch (RemoteException e) {
-		    e.printStackTrace();
-		}
-	    }
-	    bindView(v, c);
-	    return v;
-	}
+		 /**
+		  * {@inheritDoc}
+		  */
+		 @Override
+		 public View getView(int position, View convertView, ViewGroup parent) {
+			 View v = convertView;
+			 if (convertView == null) {
+				 v = mInflater.inflate(R.layout.contactlistcontact, null);
+			 }
+			 Contact c = mListContact.get(position);
+			 if (mRoster != null) {
+				 try {
+					 c = mRoster.getContact(c.getJID());
+				 } catch (RemoteException e) {
+					 e.printStackTrace();
+				 }
+			 }
+			 bindView(v, c);
+			 return v;
+		 }
 
-	@Override
-	public Filter getFilter() {
-	    return mFilter;
-	}
+		 @Override
+		 public Filter getFilter() {
+			 return mFilter;
+		 }
 
-	/**
-	 * Adapte curContact to the view.
-	 * @param view the row view.
-	 * @param curContact the current contact.
-	 */
-	private void bindView(View view, Contact curContact) {
-	    if (curContact != null) {
-		TextView v = (TextView) view.findViewById(R.id.contactlistpseudo);
-		v.setText(curContact.getName());
-		v = (TextView) view.findViewById(R.id.contactlistmsgperso);
-		v.setText(curContact.getMsgState());
-		ImageView img = (ImageView) view.findViewById(R.id.avatar);
-		String avatarId = curContact.getAvatarId();
-		int contactStatus = curContact.getStatus();
-		Drawable avatar = getAvatarStatusDrawable(curContact.getAvatarId());
-		img.setImageDrawable(avatar);
-		img.setImageLevel(contactStatus);
-	    }
-	}
+		 /**
+		  * Adapte curContact to the view.
+		  * @param view the row view.
+		  * @param curContact the current contact.
+		  */
+		 private void bindView(View view, Contact curContact) {
+			 if (curContact != null) {
+				 TextView v = (TextView) view.findViewById(R.id.contactlistpseudo);
+				 v.setText(curContact.getName());
+				 v = (TextView) view.findViewById(R.id.contactlistmsgperso);
+				 v.setText(curContact.getMsgState());
+				 ImageView img = (ImageView) view.findViewById(R.id.avatar);
+				 String avatarId = curContact.getAvatarId();
+				 int contactStatus = curContact.getStatus();
+				 Drawable avatar = getAvatarStatusDrawable(curContact.getAvatarId());
+				 img.setImageDrawable(avatar);
+				 img.setImageLevel(contactStatus);
+			 }
+		 }
 
-	/**
-	 * Get a LayerDrawable containing the avatar and the status icon.
-	 * The status icon will change with the level of the drawable.
-	 * @param avatarId the avatar id to retrieve or null to get default
-	 * @return a LayerDrawable
-	 */
-	private Drawable getAvatarStatusDrawable(String avatarId) {
-	    Drawable avatarDrawable = null;
-	    try {
-		byte[] avatar = mXmppFacade.getAvatar(avatarId);
-		if (avatar != null) {
-		    ByteArrayInputStream in = new ByteArrayInputStream(avatar);
-		    avatarDrawable = Drawable.createFromStream(in, avatarId);
-		}
-	    } catch (RemoteException e) {
-		Log.e(TAG, "Error while setting the avatar", e);
-	    }
-	    if (avatarDrawable == null)
-		avatarDrawable = getResources().getDrawable(R.drawable.beem_launcher_icon_silver);
-	    LayerDrawable ld = (LayerDrawable) getResources().getDrawable(R.drawable.avatar_status);
-	    ld.setLayerInset(1, 36, 36, 0, 0);
-	    ld.setDrawableByLayerId(R.id.avatar, avatarDrawable);
-	    return ld;
-	}
+		 /**
+		  * Get a LayerDrawable containing the avatar and the status icon.
+		  * The status icon will change with the level of the drawable.
+		  * @param avatarId the avatar id to retrieve or null to get default
+		  * @return a LayerDrawable
+		  */
+		 private Drawable getAvatarStatusDrawable(String avatarId) {
+			 Drawable avatarDrawable = null;
+			 try {
+				 byte[] avatar = mXmppFacade.getAvatar(avatarId);
+				 if (avatar != null) {
+					 ByteArrayInputStream in = new ByteArrayInputStream(avatar);
+					 avatarDrawable = Drawable.createFromStream(in, avatarId);
+				 }
+			 } catch (RemoteException e) {
+				 Log.e(TAG, "Error while setting the avatar", e);
+			 }
+			 if (avatarDrawable == null)
+				 avatarDrawable = getResources().getDrawable(R.drawable.beem_launcher_icon_silver);
+			 LayerDrawable ld = (LayerDrawable) getResources().getDrawable(R.drawable.avatar_status);
+			 ld.setLayerInset(1, 36, 36, 0, 0);
+			 ld.setDrawableByLayerId(R.id.avatar, avatarDrawable);
+			 return ld;
+		 }
 
-	/**
-	 * A Filter which select Contact to display by searching in ther Jid.
-	 */
-	private class ContactFilter extends Filter {
+		 /**
+		  * A Filter which select Contact to display by searching in ther Jid.
+		  */
+		 private class ContactFilter extends Filter {
 
-	    /**
-	     * Create a ContactFilter.
-	     */
-	    public ContactFilter() { }
+			 /**
+			  * Create a ContactFilter.
+			  */
+			 public ContactFilter() { }
 
-	    @Override
-	    protected Filter.FilterResults performFiltering(CharSequence constraint) {
-		Log.d(TAG, "performFiltering");
-		List<Contact> result = mListContact;
-		if (constraint.length() > 0) {
-		    result = new LinkedList<Contact>();
-		    for (Contact c : mContactOnGroup.get(mSelectedGroup)) {
-			if (c.getJID().contains(constraint))
-			    result.add(c);
-		    }
-		}
-		Filter.FilterResults fr = new Filter.FilterResults();
-		fr.values = result;
-		fr.count = result.size();
-		return fr;
-	    }
+			 @Override
+			 protected Filter.FilterResults performFiltering(CharSequence constraint) {
+				 Log.d(TAG, "performFiltering");
+				 List<Contact> result = mListContact;
+				 if (constraint.length() > 0) {
+					 result = new LinkedList<Contact>();
+					 for (Contact c : mContactOnGroup.get(mSelectedGroup)) {
+						 if (c.getJID().contains(constraint))
+							 result.add(c);
+					 }
+				 }
+				 Filter.FilterResults fr = new Filter.FilterResults();
+				 fr.values = result;
+				 fr.count = result.size();
+				 return fr;
+			 }
 
-	    @Override
-	    protected void publishResults(CharSequence constraint, Filter.FilterResults  results) {
-		Log.d(TAG, "publishResults");
-		List<Contact> contacts = (List<Contact>) results.values;
-		mListContact = contacts;
-		notifyDataSetChanged();
-	    }
-	}
-    }
+			 @Override
+			 protected void publishResults(CharSequence constraint, Filter.FilterResults  results) {
+				 Log.d(TAG, "publishResults");
+				 List<Contact> contacts = (List<Contact>) results.values;
+				 mListContact = contacts;
+				 notifyDataSetChanged();
+			 }
+		 }
+	 }
 
-    /**
-     * Adapter banner list.
-     */
-    private static class BeemBanner extends BaseAdapter {
-	private List<String> mGroups;
-	private LayoutInflater mInflater;
+	 /**
+	  * Adapter banner list.
+	  */
+	 private static class BeemBanner extends BaseAdapter {
+		 private List<String> mGroups;
+		 private LayoutInflater mInflater;
 
-	/**
-	 * Constructor.
-	 * @param inflater the inflater use to create the view for the banner
-	 * @param groups list of the differents groups to adapt
-	 */
-	public BeemBanner(final LayoutInflater inflater, final List<String> groups) {
-	    mGroups = groups;
-	    mInflater = inflater;
-	}
+		 /**
+		  * Constructor.
+		  * @param inflater the inflater use to create the view for the banner
+		  * @param groups list of the differents groups to adapt
+		  */
+		 public BeemBanner(final LayoutInflater inflater, final List<String> groups) {
+			 mGroups = groups;
+			 mInflater = inflater;
+		 }
 
-	@Override
-	public int getCount() {
-	    return mGroups.size();
-	}
+		 @Override
+		 public int getCount() {
+			 return mGroups.size();
+		 }
 
-	@Override
-	public Object getItem(int position) {
-	    return mGroups.get(position);
-	}
+		 @Override
+		 public Object getItem(int position) {
+			 return mGroups.get(position);
+		 }
 
-	@Override
-	public long getItemId(int position) {
-	    return position;
-	}
+		 @Override
+		 public long getItemId(int position) {
+			 return position;
+		 }
 
-	@Override
-	public View getView(int position, View convertView, ViewGroup parent) {
-	    View v = convertView;
-	    if (convertView == null) {
-		v = mInflater.inflate(R.layout.contactlist_group, null);
-	    }
-	    ((TextView) v).setText(mGroups.get(position));
-	    return v;
-	}
-    }
+		 @Override
+		 public View getView(int position, View convertView, ViewGroup parent) {
+			 View v = convertView;
+			 if (convertView == null) {
+				 v = mInflater.inflate(R.layout.contactlist_group, null);
+			 }
+			 ((TextView) v).setText(mGroups.get(position));
+			 return v;
+		 }
+	 }
 
-    /**
-     * The service connection used to connect to the Beem service.
-     */
-    private class BeemServiceConnection implements ServiceConnection {
+	 /**
+	  * The service connection used to connect to the Beem service.
+	  */
+	 private class BeemServiceConnection implements ServiceConnection {
 
-	/**
-	 * Constructor.
-	 */
-	public BeemServiceConnection() {
-	}
+		 /**
+		  * Constructor.
+		  */
+		 public BeemServiceConnection() {
+		 }
 
-	@Override
-	public void onServiceConnected(ComponentName name, IBinder service) {
-	    mXmppFacade = IXmppFacade.Stub.asInterface(service);
-	    try {
-		mRoster = mXmppFacade.getRoster();
-		if (mRoster != null) {
-		    List<String> tmpGroupList = mRoster.getGroupsNames();
-		    Collections.sort(tmpGroupList);
-		    mListGroup.clear();
-		    mListGroup.add(getString(R.string.contact_list_all_contact));
-		    mListGroup.addAll(tmpGroupList);
-		    mListGroup.add(getString(R.string.contact_list_no_group));
-		    assignContactToGroups(mRoster.getContactList(), tmpGroupList);
-		    makeSortedList(mContactOnGroup);
-		    if (!mSettings.getBoolean("settings_key_hide_groups", false))
-			showGroups();
-		    else
-			hideGroups();
-		    String group = getString(R.string.contact_list_all_contact);
-		    buildContactList(group);
-		    mRoster.addRosterListener(mBeemRosterListener);
-		    Log.d(TAG, "add roster listener");
-		    mChatManager = mXmppFacade.getChatManager();
-		}
-	    } catch (RemoteException e) {
-		e.printStackTrace();
-	    }
-	}
+		 @Override
+		 public void onServiceConnected(ComponentName name, IBinder service) {
+			 mXmppFacade = IXmppFacade.Stub.asInterface(service);
+			 try {
+				 mRoster = mXmppFacade.getRoster();
+				 if (mRoster != null) {
+					 List<String> tmpGroupList = mRoster.getGroupsNames();
+					 Collections.sort(tmpGroupList);
+					 mListGroup.clear();
+					 mListGroup.add(getString(R.string.contact_list_all_contact));
+					 mListGroup.addAll(tmpGroupList);
+					 mListGroup.add(getString(R.string.contact_list_no_group));
+					 assignContactToGroups(mRoster.getContactList(), tmpGroupList);
+					 makeSortedList(mContactOnGroup);
+					 if (!mSettings.getBoolean("settings_key_hide_groups", false))
+						 showGroups();
+					 else
+						 hideGroups();
+					 String group = getString(R.string.contact_list_all_contact);
+					 buildContactList(group);
+					 mRoster.addRosterListener(mBeemRosterListener);
+					 Log.d(TAG, "add roster listener");
+					 mChatManager = mXmppFacade.getChatManager();
+				 }
+			 } catch (RemoteException e) {
+				 e.printStackTrace();
+			 }
+		 }
 
-	@Override
-	public void onServiceDisconnected(ComponentName name) {
-	    try {
-		mRoster.removeRosterListener(mBeemRosterListener);
-	    } catch (RemoteException e) {
-		e.printStackTrace();
-	    }
-	    mXmppFacade = null;
-	    mChatManager = null;
-	    mRoster = null;
-	    mListContact.clear();
-	    mListGroup.clear();
-	    mContactOnGroup.clear();
-	    mBinded = false;
-	}
+		 @Override
+		 public void onServiceDisconnected(ComponentName name) {
+			 try {
+				 mRoster.removeRosterListener(mBeemRosterListener);
+			 } catch (RemoteException e) {
+				 e.printStackTrace();
+			 }
+			 mXmppFacade = null;
+			 mChatManager = null;
+			 mRoster = null;
+			 mListContact.clear();
+			 mListGroup.clear();
+			 mContactOnGroup.clear();
+			 mBinded = false;
+		 }
 
-	/**
-	 * Assign the differents contact to their groups.
-	 * This methods will fill the mContactOnGroup map.
-	 *
-	 * @param contacts list of contacts
-	 * @param groupNames list of existing groups
-	 */
-	private void assignContactToGroups(List<Contact> contacts, List<String> groupNames) {
-	    boolean hideDisconnected = mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false);
-	    mContactOnGroup.clear();
-	    List<Contact> all = new LinkedList<Contact>();
-	    List<Contact> noGroups = new LinkedList<Contact>();
-	    for (String group : groupNames) {
-		mContactOnGroup.put(group, new LinkedList<Contact>());
-	    }
-	    for (Contact c : contacts) {
-		if (hideDisconnected && !Status.statusOnline(c.getStatus())) {
-		    continue;
-		}
-		all.add(c);
-		List<String> groups = c.getGroups();
-		if (groups.isEmpty())
-		    noGroups.add(c);
-		else {
-		    for (String currentGroup : groups) {
-			List<Contact> contactsByGroups = mContactOnGroup.get(currentGroup);
-			contactsByGroups.add(c);
-		    }
-		}
-	    }
-	    mContactOnGroup.put(getString(R.string.contact_list_no_group), noGroups);
-	    mContactOnGroup.put(getString(R.string.contact_list_all_contact), all);
-	}
+		 /**
+		  * Assign the differents contact to their groups.
+		  * This methods will fill the mContactOnGroup map.
+		  *
+		  * @param contacts list of contacts
+		  * @param groupNames list of existing groups
+		  */
+		 private void assignContactToGroups(List<Contact> contacts, List<String> groupNames) {
+			 boolean hideDisconnected = mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false);
+			 mContactOnGroup.clear();
+			 List<Contact> all = new LinkedList<Contact>();
+			 List<Contact> noGroups = new LinkedList<Contact>();
+			 for (String group : groupNames) {
+				 mContactOnGroup.put(group, new LinkedList<Contact>());
+			 }
+			 for (Contact c : contacts) {
+				 if (hideDisconnected && !Status.statusOnline(c.getStatus())) {
+					 continue;
+				 }
+				 all.add(c);
+				 List<String> groups = c.getGroups();
+				 if (groups.isEmpty())
+					 noGroups.add(c);
+				 else {
+					 for (String currentGroup : groups) {
+						 List<Contact> contactsByGroups = mContactOnGroup.get(currentGroup);
+						 contactsByGroups.add(c);
+					 }
+				 }
+			 }
+			 mContactOnGroup.put(getString(R.string.contact_list_no_group), noGroups);
+			 mContactOnGroup.put(getString(R.string.contact_list_all_contact), all);
+		 }
 
-	/**
-	 * Make the List of the map became Insertion sorted list.
-	 *
-	 * @param map the map to convert.
-	 */
-	private void makeSortedList(Map<String, List<Contact>> map) {
-	    for (Map.Entry<String, List<Contact>> entry : map.entrySet()) {
-		List<Contact> l = entry.getValue();
-		entry.setValue(new SortedList<Contact>(l, mComparator));
-	    }
-	}
-    }
+		 /**
+		  * Make the List of the map became Insertion sorted list.
+		  *
+		  * @param map the map to convert.
+		  */
+		 private void makeSortedList(Map<String, List<Contact>> map) {
+			 for (Map.Entry<String, List<Contact>> entry : map.entrySet()) {
+				 List<Contact> l = entry.getValue();
+				 entry.setValue(new SortedList<Contact>(l, mComparator));
+			 }
+		 }
+	 }
 
 
 
 
-    /**
-     * Comparator Contact by status and name.
-     */
-    private static class ComparatorContactListByStatusAndName<T> implements Comparator<T> {
-	/**
-	 * Constructor.
-	 */
-	public ComparatorContactListByStatusAndName() {
-	}
+	 /**
+	  * Comparator Contact by status and name.
+	  */
+	 private static class ComparatorContactListByStatusAndName<T> implements Comparator<T> {
+		 /**
+		  * Constructor.
+		  */
+		 public ComparatorContactListByStatusAndName() {
+		 }
 
-	@Override
-	public int compare(T c1, T c2) {
-	    if (((Contact) c1).getStatus() < ((Contact) c2).getStatus()) {
-		return 1;
-	    } else if (((Contact) c1).getStatus() > ((Contact) c2).getStatus()) {
-		return -1;
-	    } else
-		return ((Contact) c1).getName().compareToIgnoreCase(((Contact) c2).getName());
-	}
-    }
+		 @Override
+		 public int compare(T c1, T c2) {
+			 if (((Contact) c1).getStatus() < ((Contact) c2).getStatus()) {
+				 return 1;
+			 } else if (((Contact) c1).getStatus() > ((Contact) c2).getStatus()) {
+				 return -1;
+			 } else
+				 return ((Contact) c1).getName().compareToIgnoreCase(((Contact) c2).getName());
+		 }
+	 }
 
-    /**
-     * Event simple click on item of the contact list.
-     */
-    private class BeemContactListOnClick implements OnItemClickListener {
-	/**
-	 * Constructor.
-	 */
-	public BeemContactListOnClick() {
-	}
+	 /**
+	  * Event simple click on item of the contact list.
+	  */
+	 private class BeemContactListOnClick implements OnItemClickListener {
+		 /**
+		  * Constructor.
+		  */
+		 public BeemContactListOnClick() {
+		 }
 
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void onItemClick(AdapterView<?> arg0, View v, int pos, long lpos) {
-	    Contact c = mListContact.get(pos);
-	    Intent i = new Intent(ContactList.this, Chat.class);
-	    i.setData(c.toUri());
-	    startActivity(i);
-	}
-    }
+		 /**
+		  * {@inheritDoc}
+		  */
+		 @Override
+		 public void onItemClick(AdapterView<?> arg0, View v, int pos, long lpos) {
+			 Contact c = mListContact.get(pos);
+			 Intent i = new Intent(ContactList.this, Chat.class);
+			 i.setData(c.toUri());
+			 startActivity(i);
+		 }
+	 }
 
-    /**
-     * Event simple click on middle groupe name.
-     */
-    private class OnItemClickGroupName implements OnItemClickListener {
+	 /**
+	  * Event simple click on middle groupe name.
+	  */
+	 private class OnItemClickGroupName implements OnItemClickListener {
 
-	/**
-	 * Constructor.
-	 */
-	public OnItemClickGroupName() {
-	}
+		 /**
+		  * Constructor.
+		  */
+		 public OnItemClickGroupName() {
+		 }
 
-	@Override
-	public void onItemClick(AdapterView<?> arg0, View v, int i, long l) {
-	    String group = mListGroup.get(i);
-	    buildContactList(group);
-	}
-    }
+		 @Override
+		 public void onItemClick(AdapterView<?> arg0, View v, int i, long l) {
+			 String group = mListGroup.get(i);
+			 buildContactList(group);
+		 }
+	 }
 
 }