# HG changeset patch # User Nikita Kozlov # Date 1293321718 -3600 # Node ID 322071d17c001d2e59c914d1386b7379532039c9 # Parent 0e5b95573614f6f4a639cf3fea5fe005f76cf637 merge beem-audio with trunk + some bugfix/improvements diff -r 0e5b95573614 -r 322071d17c00 doc/asmack-beem/beem-build-process.patch --- 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" diff -r 0e5b95573614 -r 322071d17c00 doc/asmack-beem/beem_patches/50-jingle-ext.patch --- /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(); diff -r 0e5b95573614 -r 322071d17c00 doc/asmack-beem/beem_patches/50-remove-jingle_mediaimpl.patch --- /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 +- *

+- * Copyright 2003-2006 Jive Software. +- *

+- * 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 +- *

+- * 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.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 +- *

+- * Copyright 2003-2006 Jive Software. +- *

+- * 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 +- *

+- * 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.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 payloads = new ArrayList(); +- +- private PayloadType preferredPayloadType = null; +- +- public TestMediaManager(JingleTransportManager transportManager) { +- super(transportManager); +- } +- +- /** +- * Return all supported Payloads for this Manager. +- * +- * @return The Payload List +- */ +- public List getPayloads() { +- return payloads; +- } +- +- public void setPayloads(List 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 +- *

+- * Copyright 2003-2006 Jive Software. +- *

+- * 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 +- *

+- * 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.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 mediaManagers = new ArrayList(); +- //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 +- *

+- * Copyright 2003-2006 Jive Software. +- *

+- * 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 +- *

+- * 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; +- +-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 +- *

+- * Copyright 2003-2006 Jive Software. +- *

+- * 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 +- *

+- * 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; +- +-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 payloads = new ArrayList(); +- +- 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 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 +- *

+- * Copyright 2003-2006 Jive Software. +- *

+- * 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 +- *

+- * 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.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 managers = new ArrayList(); +- +- 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 getPayloads() { +- List list = new ArrayList(); +- 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 +- *

+- * Copyright 2003-2006 Jive Software. +- *

+- * 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 +- *

+- * 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.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 +- *

+- * Copyright 2003-2006 Jive Software. +- *

+- * 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 +- *

+- * 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.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 +- *

+- * Copyright 2003-2006 Jive Software. +- *

+- * 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 +- *

+- * 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.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. +- * This API only currently works on windows and Mac. +- * +- * @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 payloads = new ArrayList(); +- 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 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 +- *

+- * Copyright 2003-2006 Jive Software. +- *

+- * 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 +- *

+- * 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.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. +- *

+- * Send from portA to portB and receive from portB in portA. +- *

+- * Sending +- * portA ---> portB +- *

+- * Receiving +- * portB ---> portA +- *

+- * Transmit and Receive are interdependents. To receive you MUST trasmit. +- * +- * @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 sendStreams = new ArrayList(); +- +- 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 +- *

+- * Copyright 2003-2006 Jive Software. +- *

+- * 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 +- *

+- * 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.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 +- *

+- * Copyright 2003-2006 Jive Software. +- *

+- * 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 +- *

+- * 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.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. +- * This API only currently works on windows. +- * +- * @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 payloads = new ArrayList(); +- +- 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 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 +- *

+- * Copyright 2003-2006 Jive Software. +- *

+- * 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 +- *

+- * 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.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; +- } +-} diff -r 0e5b95573614 -r 322071d17c00 doc/asmack-beem/lib/jstun.jar Binary file doc/asmack-beem/lib/jstun.jar has changed diff -r 0e5b95573614 -r 322071d17c00 libs/asmack-android-7-beem.jar Binary file libs/asmack-android-7-beem.jar has changed diff -r 0e5b95573614 -r 322071d17c00 res/menu/contactlist_context.xml --- 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 @@ + android:title="@string/CDCall" android:visible="true" + >

diff -r 0e5b95573614 -r 322071d17c00 src/com/beem/project/beem/jingle/JingleService.java --- 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++) { diff -r 0e5b95573614 -r 322071d17c00 src/com/beem/project/beem/jingle/MicrophoneRTPManager.java --- 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(); - 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)); } } diff -r 0e5b95573614 -r 322071d17c00 src/com/beem/project/beem/service/XmppFacade.java --- 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 diff -r 0e5b95573614 -r 322071d17c00 src/com/beem/project/beem/ui/Call.java --- 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; } } } diff -r 0e5b95573614 -r 322071d17c00 src/com/beem/project/beem/ui/ContactList.java --- 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 mListGroup = new ArrayList(); - - /** 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> mContactOnGroup = new HashMap>(); - 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 mComparator = - new ComparatorContactListByStatusAndName(); - private final BeemRosterListener mBeemRosterListener = new BeemRosterListener(); - private List 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 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 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(); - 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 : - *
    - *
  • add him to his groups
  • - *
  • add him to the specials groups - *
- */ - @Override - public void onEntriesAdded(final List 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 groups = contact.getGroups(); - if (visible) { - for (String group : groups) { - if (!mListGroup.contains(group)) { - mListGroup.add(mListGroup.size() - 1, group); - List tmplist = new SortedList(new LinkedList(), mComparator); - mContactOnGroup.put(group, tmplist); - } - List 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. - *
    - *
  • Remove the contact from all groups
  • - *
- */ - @Override - public void onEntriesDeleted(final List addresses) throws RemoteException { - Log.d(TAG, "onEntries deleted " + addresses); - for (String cToDelete : addresses) { - Contact contact = new Contact(cToDelete); - for (Map.Entry> entry : mContactOnGroup.entrySet()) { - List 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. - *
    - *
  • Remove the contact from all groups
  • - *
  • if contact has to be shown add it to his groups
  • - *
  • if contact has to be shown add it to the specials groups
  • - *
- */ - @Override - public void onEntriesUpdated(final List 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 groups = contact.getGroups(); - for (Map.Entry> entry : mContactOnGroup.entrySet()) { - List 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 tmplist = new SortedList( - new LinkedList(), 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 mListGroup = new ArrayList(); - // add the contact to all and no groups - if (visible) { - addToSpecialList(contact); - } - } - cleanBannerGroup(); - } - - /** - * {@inheritDoc} - * Simple stategy to handle the onPresenceChanged event. - *
    - *
  • Remove the contact from all groups
  • - *
  • if contact has to be shown add it to his groups
  • - *
  • if contact has to be shown add it to the specials groups
  • - *
- */ - @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 groups = contact.getGroups(); - for (Map.Entry> entry : mContactOnGroup.entrySet()) { - List 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 groups = contact.getGroups(); - List 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 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 rosterGroups = mRoster.getGroupsNames(); - List 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> mContactOnGroup = new HashMap>(); + 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 mComparator = + new ComparatorContactListByStatusAndName(); + private final BeemRosterListener mBeemRosterListener = new BeemRosterListener(); + private List 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 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 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(); + 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 : + *
    + *
  • add him to his groups
  • + *
  • add him to the specials groups + *
+ */ + @Override + public void onEntriesAdded(final List 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 groups = contact.getGroups(); + if (visible) { + for (String group : groups) { + if (!mListGroup.contains(group)) { + mListGroup.add(mListGroup.size() - 1, group); + List tmplist = new SortedList(new LinkedList(), mComparator); + mContactOnGroup.put(group, tmplist); + } + List 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. + *
    + *
  • Remove the contact from all groups
  • + *
+ */ + @Override + public void onEntriesDeleted(final List addresses) throws RemoteException { + Log.d(TAG, "onEntries deleted " + addresses); + for (String cToDelete : addresses) { + Contact contact = new Contact(cToDelete); + for (Map.Entry> entry : mContactOnGroup.entrySet()) { + List 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. + *
    + *
  • Remove the contact from all groups
  • + *
  • if contact has to be shown add it to his groups
  • + *
  • if contact has to be shown add it to the specials groups
  • + *
+ */ + @Override + public void onEntriesUpdated(final List 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 groups = contact.getGroups(); + for (Map.Entry> entry : mContactOnGroup.entrySet()) { + List 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 tmplist = new SortedList( + new LinkedList(), 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. + *
    + *
  • Remove the contact from all groups
  • + *
  • if contact has to be shown add it to his groups
  • + *
  • if contact has to be shown add it to the specials groups
  • + *
+ */ + @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 groups = contact.getGroups(); + for (Map.Entry> entry : mContactOnGroup.entrySet()) { + List 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 groups = contact.getGroups(); + List 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 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 rosterGroups = mRoster.getGroupsNames(); + List 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 result = mListContact; - if (constraint.length() > 0) { - result = new LinkedList(); - 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 result = mListContact; + if (constraint.length() > 0) { + result = new LinkedList(); + 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 contacts = (List) results.values; - mListContact = contacts; - notifyDataSetChanged(); - } - } - } + @Override + protected void publishResults(CharSequence constraint, Filter.FilterResults results) { + Log.d(TAG, "publishResults"); + List contacts = (List) results.values; + mListContact = contacts; + notifyDataSetChanged(); + } + } + } - /** - * Adapter banner list. - */ - private static class BeemBanner extends BaseAdapter { - private List mGroups; - private LayoutInflater mInflater; + /** + * Adapter banner list. + */ + private static class BeemBanner extends BaseAdapter { + private List 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 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 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 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 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 contacts, List groupNames) { - boolean hideDisconnected = mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false); - mContactOnGroup.clear(); - List all = new LinkedList(); - List noGroups = new LinkedList(); - for (String group : groupNames) { - mContactOnGroup.put(group, new LinkedList()); - } - for (Contact c : contacts) { - if (hideDisconnected && !Status.statusOnline(c.getStatus())) { - continue; - } - all.add(c); - List groups = c.getGroups(); - if (groups.isEmpty()) - noGroups.add(c); - else { - for (String currentGroup : groups) { - List 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 contacts, List groupNames) { + boolean hideDisconnected = mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false); + mContactOnGroup.clear(); + List all = new LinkedList(); + List noGroups = new LinkedList(); + for (String group : groupNames) { + mContactOnGroup.put(group, new LinkedList()); + } + for (Contact c : contacts) { + if (hideDisconnected && !Status.statusOnline(c.getStatus())) { + continue; + } + all.add(c); + List groups = c.getGroups(); + if (groups.isEmpty()) + noGroups.add(c); + else { + for (String currentGroup : groups) { + List 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> map) { - for (Map.Entry> entry : map.entrySet()) { - List l = entry.getValue(); - entry.setValue(new SortedList(l, mComparator)); - } - } - } + /** + * Make the List of the map became Insertion sorted list. + * + * @param map the map to convert. + */ + private void makeSortedList(Map> map) { + for (Map.Entry> entry : map.entrySet()) { + List l = entry.getValue(); + entry.setValue(new SortedList(l, mComparator)); + } + } + } - /** - * Comparator Contact by status and name. - */ - private static class ComparatorContactListByStatusAndName implements Comparator { - /** - * Constructor. - */ - public ComparatorContactListByStatusAndName() { - } + /** + * Comparator Contact by status and name. + */ + private static class ComparatorContactListByStatusAndName implements Comparator { + /** + * 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); + } + } }