Android平臺下基於XMPP的IM研究(二 MultiUserChat 聊天室)

yangxi_001發表於2014-08-06
一直想寫東西,但是上班不能在CSDN上寫東西。下班回家又忙著學Iphone開發,唉,兩個個字,忒忙。今天就寫寫Smack的聊天室功能吧。

先上程式碼:

[java] view plaincopy
  1. import java.io.BufferedReader;   
  2. import java.io.InputStreamReader;   
  3. import java.util.ArrayList;   
  4. import java.util.Collection;   
  5. import java.util.Iterator;   
  6. import java.util.List;   
  7.   
  8. import org.jivesoftware.smack.Chat;   
  9. import org.jivesoftware.smack.ConnectionConfiguration;   
  10. import org.jivesoftware.smack.MessageListener;   
  11. import org.jivesoftware.smack.PacketListener;   
  12. import org.jivesoftware.smack.SmackConfiguration;   
  13. import org.jivesoftware.smack.XMPPConnection;   
  14. import org.jivesoftware.smack.XMPPException;   
  15. import org.jivesoftware.smack.packet.Message;   
  16. import org.jivesoftware.smack.packet.Packet;   
  17. import org.jivesoftware.smack.provider.ProviderManager;   
  18. import org.jivesoftware.smackx.Form;   
  19. import org.jivesoftware.smackx.FormField;   
  20. import org.jivesoftware.smackx.ServiceDiscoveryManager;   
  21. import org.jivesoftware.smackx.muc.DefaultParticipantStatusListener;   
  22. import org.jivesoftware.smackx.muc.DefaultUserStatusListener;   
  23. import org.jivesoftware.smackx.muc.DiscussionHistory;   
  24. import org.jivesoftware.smackx.muc.HostedRoom;   
  25. import org.jivesoftware.smackx.muc.InvitationListener;   
  26. import org.jivesoftware.smackx.muc.InvitationRejectionListener;   
  27. import org.jivesoftware.smackx.muc.MultiUserChat;   
  28. import org.jivesoftware.smackx.muc.RoomInfo;   
  29. import org.jivesoftware.smackx.muc.SubjectUpdatedListener;   
  30. import org.jivesoftware.smackx.packet.ChatStateExtension;   
  31. import org.jivesoftware.smackx.packet.DiscoverInfo;   
  32. import org.jivesoftware.smackx.packet.DiscoverItems;   
  33. import org.jivesoftware.smackx.packet.OfflineMessageInfo;   
  34. import org.jivesoftware.smackx.packet.OfflineMessageRequest;   
  35. import org.jivesoftware.smackx.provider.AdHocCommandDataProvider;   
  36. import org.jivesoftware.smackx.provider.BytestreamsProvider;   
  37. import org.jivesoftware.smackx.provider.DataFormProvider;   
  38. import org.jivesoftware.smackx.provider.DiscoverInfoProvider;   
  39. import org.jivesoftware.smackx.provider.DiscoverItemsProvider;   
  40. import org.jivesoftware.smackx.provider.IBBProviders;   
  41. import org.jivesoftware.smackx.provider.MUCAdminProvider;   
  42. import org.jivesoftware.smackx.provider.MUCOwnerProvider;   
  43. import org.jivesoftware.smackx.provider.MUCUserProvider;   
  44. import org.jivesoftware.smackx.provider.StreamInitiationProvider;   
  45. import org.jivesoftware.smackx.provider.VCardProvider;   
  46. import org.jivesoftware.smackx.provider.XHTMLExtensionProvider;   
  47.   
  48. public class TestSmack2 {   
  49.     public static void main(String[] args) {XMPPConnection.DEBUG_ENABLED = true;   
  50.         final ConnectionConfiguration connectionConfig = new ConnectionConfiguration("PC2010102716"5222"");  
  51.         connectionConfig.setSASLAuthenticationEnabled(false);   
  52.         ProviderManager pm = ProviderManager.getInstance();   
  53.         configure(pm);   
  54.         try {   
  55.             XMPPConnection connection = new XMPPConnection(connectionConfig);   
  56.             connection.connect();//連線   
  57.             initFeatures(connection);   
  58.             connection.login("test""test");//登陸   
  59.             //聊天室   
  60.             //MultiUserChat multiUserChat = new MultiUserChat(connection, new InvitationListener() {});  
  61.              //查詢服務   
  62.             System.out.println(connection.getServiceName());   
  63.             List<String> col = getConferenceServices(connection.getServiceName(), connection);  
  64.             for (Object aCol : col) {   
  65.                 String service = (String) aCol;   
  66.                  //查詢伺服器上的聊天室   
  67.                 Collection<HostedRoom> rooms = MultiUserChat.getHostedRooms(connection, service);  
  68.                 for(HostedRoom room : rooms) {   
  69.                     //檢視Room訊息   
  70.                     System.out.println(room.getName() + " - " +room.getJid());   
  71.                     RoomInfo roomInfo = MultiUserChat.getRoomInfo(connection, room.getJid());  
  72.                     if(roomInfo != null) {   
  73.                         System.out.println(roomInfo.getOccupantsCount() + " : " + roomInfo.getSubject());  
  74.                     }     
  75.                  }     
  76.             }   
  77.           
  78.             /*---建立預設配置的聊天室 ---  
  79.             先看看官方的文件:  
  80.             Creates a new multi user chat with the specified connection and room name. Note: no 
  81.                   * information is sent to or received from the server until you attempt to  
  82.                  * {@link #join(String) join} the chat room. On some server implementations, 
  83.                   * the room will not be created until the first person joins it  
  84.                  * 最重要一句:直到使用者呼叫join方法的時候聊天室才會被建立  
  85.                  */   
  86.             MultiUserChat muc = new MultiUserChat(connection, "instant@conference.pc2010102716");  
  87.              muc.create("user1");   
  88.             muc.sendConfigurationForm(new Form(Form.TYPE_SUBMIT));   
  89.           
  90.             //----建立手動配置聊天室----   
  91.             muc = new MultiUserChat(connection, "reserved4@conference.pc2010102716");   
  92.           
  93.             //銷燬聊天室   
  94.             //muc.destroy("Test", null);   
  95.             muc.create("user2");   
  96.             //獲取聊天室的配置表單   
  97.             Form form = muc.getConfigurationForm();   
  98.             //根據原始表單建立一個要提交的新表單   
  99.             Form submitForm = form.createAnswerForm();   
  100.             //向提交的表單新增預設答覆   
  101.             for(Iterator<FormField> fields = form.getFields(); fields.hasNext();) {   
  102.                 FormField field = (FormField) fields.next();   
  103.                 if(!FormField.TYPE_HIDDEN.equals(field.getType()) && field.getVariable() != null) {  
  104.                     submitForm.setDefaultAnswer(field.getVariable());   
  105.                 }   
  106.             }   
  107.             //重新設定聊天室名稱   
  108.             submitForm.setAnswer("muc#roomconfig_roomname""Reserved4 Room");   
  109.             //設定聊天室的新擁有者   
  110.             List<String> owners = new ArrayList<String>();   
  111.             owners.add("test@pc2010102716");   
  112.             submitForm.setAnswer("muc#roomconfig_roomowners", owners);   
  113.             //設定密碼   
  114.             submitForm.setAnswer("muc#roomconfig_passwordprotectedroom"true);   
  115.             submitForm.setAnswer("muc#roomconfig_roomsecret""reserved");   
  116.             //設定描述   
  117.             submitForm.setAnswer("muc#roomconfig_roomdesc""新建立的reserved聊天室");   
  118.             //設定聊天室是持久聊天室,即將要被儲存下來   
  119.             //submitForm.setAnswer("muc#roomconfig_persistentroom", true);   
  120.             //傳送已完成的表單到伺服器配置聊天室   
  121.             muc.sendConfigurationForm(submitForm);   
  122.           
  123.             //加入聊天室(使用暱稱喝醉的毛毛蟲 ,使用密碼ddd)   
  124.             muc = new MultiUserChat(connection, "ddd@conference.pc2010102716");   
  125.             muc.join("喝醉的毛毛蟲""ddd");   
  126.           
  127.             //監聽訊息   
  128.             muc.addMessageListener(new PacketListener() {   
  129.                 @Override   
  130.                 public void processPacket(Packet packet) {   
  131.                     Message message = (Message) packet;   
  132.                     System.out.println(message.getFrom() + " : " + message.getBody());;   
  133.                 }   
  134.             });   
  135.           
  136.             //muc = new MultiUserChat(connection, "ddd@conference.pc2010102716");   
  137.             //muc.join("喝醉的毛毛蟲", "ddd");   
  138.           
  139.             //加入聊天室(使用暱稱喝醉的毛毛蟲 ,使用密碼ddd)並且獲取聊天室裡最後5條資訊,   
  140.             //注:addMessageListener監聽器必須在此join方法之前,否則無法監聽到需要的5條訊息   
  141.             muc = new MultiUserChat(connection, "ddd@conference.pc2010102716");   
  142.             DiscussionHistory history = new DiscussionHistory();   
  143.             history.setMaxStanzas(5);   
  144.             muc.join("喝醉的毛毛蟲""ddd", history, SmackConfiguration.getPacketReplyTimeout());   
  145.           
  146.             //監聽拒絕加入聊天室的使用者   
  147.             muc.addInvitationRejectionListener(new InvitationRejectionListener() {   
  148.                 @Override   
  149.                 public void invitationDeclined(String invitee, String reason) {   
  150.                 System.out.println(invitee + " reject invitation, reason is " + reason);   
  151.                 }   
  152.             });   
  153.             //邀請使用者加入聊天室   
  154.             muc.invite("test3@pc2010102716""大家來談談人生");   
  155.             //監聽邀請加入聊天室請求   
  156.             MultiUserChat.addInvitationListener(connection, new InvitationListener() {   
  157.                 @Override   
  158.                 public void invitationReceived(XMPPConnection conn, String room, String inviter, String reason, String password, Message message) {   
  159.                 //例:直接拒絕邀請   
  160.                 MultiUserChat.decline(conn, room, inviter, "你丫很閒啊!");   
  161.                 }   
  162.             });   
  163.           
  164.           
  165.             //根據roomJID獲取聊天室資訊   
  166.             RoomInfo roomInfo = MultiUserChat.getRoomInfo(connection, "ddd@conference.pc2010102716");  
  167.              System.out.println(roomInfo.getRoom() + "-" + roomInfo.getSubject() + "-" + roomInfo.getOccupantsCount());  
  168.                
  169.             //判斷使用者是否支援Multi-User聊天協議   
  170.             //注:需要加上資源識別符號   
  171.             boolean supports = MultiUserChat.isServiceEnabled(connection, "test3@pc2010102716/spark");  
  172.              //獲取某使用者所加入的聊天室   
  173.             if(supports) {   
  174.                 Iterator<String> joinedRooms = MultiUserChat.getJoinedRooms(connection, "test3@pc2010102716/spark");  
  175.                 while(joinedRooms.hasNext()) {   
  176.                     System.out.println("test3 has joined Room " + joinedRooms.next());   
  177.                 }   
  178.             }   
  179.           
  180.             //與聊天室使用者私聊   
  181.             Chat chat = muc.createPrivateChat("ddd@conference.pc2010102716/飛鳥"new MessageListener() {  
  182.                  @Override   
  183.                  public void processMessage(Chat chat, Message message) {   
  184.                      System.out.println("Private Chat: Received message from " + message.getFrom() + "-" + message.getBody());  
  185.                  }   
  186.             });   
  187.             chat.sendMessage("今天不用加班吧?");   
  188.           
  189.             //改變聊天室主題   
  190.             muc.addSubjectUpdatedListener(new SubjectUpdatedListener() {   
  191.                 @Override   
  192.                 public void subjectUpdated(String subject, String from) {   
  193.                     System.out.println("Subject updated to " + subject +" by " + from);   
  194.                 }   
  195.             });   
  196.             //muc.changeSubject("New Subject11");   
  197.           
  198.             /*一個成員可能有四種角色:  
  199.             1:主持者(Moderator) (許可權最大的角色,管理其他成員在聊天室中的角色  
  200.             2:參與者(Participant  
  201.             3:遊客 (Visitor) (不能向所有成員傳送訊息)  
  202.             4:無(沒有角色)(NONE)  
  203.             */   
  204.           
  205.             /*聊天室使用者可以有5種從屬關係  
  206.               * 1、所有者 Owner  
  207.               * 2、管理員 Admin  
  208.               * 3、成員 Member  
  209.               * 4、被驅逐者 Outcast  
  210.               * 5、無(不存在從屬關係) None  
  211.               */   
  212.           
  213.             //配置聊天室為Moderated聊天室   
  214.             form = muc.getConfigurationForm();   
  215.             Form answerForm = form.createAnswerForm();   
  216.             answerForm.setAnswer("muc#roomconfig_moderatedroom""1");   
  217.             muc.sendConfigurationForm(answerForm);   
  218.           
  219.             //監聽自己的狀態變更和事件   
  220.             muc.addUserStatusListener(new DefaultUserStatusListener() {   
  221.                 @Override   
  222.                 public void voiceRevoked() {   
  223.                     super.voiceRevoked();   
  224.                     System.out.println("你被禁言了!");   
  225.                 }   
  226.               
  227.                 @Override   
  228.                 public void voiceGranted() {   
  229.                     super.voiceGranted();   
  230.                     System.out.println("你被批准發言了!");   
  231.                 }   
  232.               
  233.                 @Override   
  234.                 public void membershipGranted() {   
  235.                     super.membershipGranted();   
  236.                     System.out.println("你被賦予了Member許可權");   
  237.                 }   
  238.               
  239.                 @Override   
  240.                 public void membershipRevoked() {   
  241.                     super.membershipRevoked();   
  242.                     System.out.println("你被解除了Member許可權");   
  243.                 }   
  244.               
  245.                 @Override   
  246.                 public void adminGranted() {   
  247.                     super.adminGranted();   
  248.                     System.out.println("你被賦予了管理員許可權");   
  249.                 }   
  250.               
  251.                 @Override   
  252.                 public void adminRevoked() {   
  253.                 super.adminRevoked();   
  254.                 System.out.println("你被解除了管理員許可權");   
  255.                 }   
  256.             //......   
  257.             });   
  258.             //房主(Owner)批准test3發言權   
  259.             muc.grantVoice("test3@pc2010102716");   
  260.           
  261.             //監聽他人狀態變更   
  262.             muc.addParticipantStatusListener(new DefaultParticipantStatusListener() {   
  263.           
  264.                 @Override   
  265.                 public void voiceGranted(String participant) {   
  266.                     super.voiceGranted(participant);   
  267.                 System.out.println(participant + "被批准發言了!");   
  268.                 }   
  269.               
  270.                 @Override   
  271.                 public void voiceRevoked(String participant) {   
  272.                     super.voiceRevoked(participant);   
  273.                     System.out.println(participant + "被禁言了!");   
  274.                 }   
  275.               
  276.                 @Override   
  277.                 public void membershipRevoked(String participant) {   
  278.                     super.membershipRevoked(participant);   
  279.                 }   
  280.               
  281.                 @Override   
  282.                 public void adminGranted(String participant) {   
  283.                     super.adminGranted(participant);   
  284.                 }   
  285.               
  286.                 @Override   
  287.                 public void adminRevoked(String participant) {   
  288.                     super.adminRevoked(participant);   
  289.                 }   
  290.           
  291.             });   
  292.           
  293.             //房主(Owner)批准test3管理員特權   
  294.             muc.grantAdmin("test3@pc2010102716");   
  295.       
  296.       
  297.             //傳送訊息   
  298.             BufferedReader cmdIn = new BufferedReader(new InputStreamReader(System.in));   
  299.             while(true) {   
  300.                 try {   
  301.                      String cmd = cmdIn.readLine();   
  302.                      if("!q".equalsIgnoreCase(cmd)) {   
  303.                          break;   
  304.                      }   
  305.                   }catch(Exception ex) {   
  306.                   }   
  307.             }   
  308.             connection.disconnect();   
  309.             System.exit(0);         
  310.         } catch (Exception e) {   
  311.             e.printStackTrace();   
  312.         }   
  313.     }   
  314.   
  315.     public static  List<String> getConferenceServices(String server, XMPPConnection connection) throws Exception {  
  316.      List<String> answer = new ArrayList<String>();   
  317.             ServiceDiscoveryManager discoManager = ServiceDiscoveryManager.getInstanceFor(connection);  
  318.              DiscoverItems items = discoManager.discoverItems(server);   
  319.             for (Iterator<DiscoverItems.Item> it = items.getItems(); it.hasNext();) {  
  320.                  DiscoverItems.Item item = (DiscoverItems.Item)it.next();   
  321.                 if (item.getEntityID().startsWith("conference") || item.getEntityID().startsWith("private")) {  
  322.                      answer.add(item.getEntityID());   
  323.                 }   
  324.                 else {   
  325.                     try {   
  326.                         DiscoverInfo info = discoManager.discoverInfo(item.getEntityID());  
  327.                          if (info.containsFeature("http://jabber.org/protocol/muc")) {  
  328.                              answer.add(item.getEntityID());   
  329.                         }   
  330.                     }   
  331.                     catch (XMPPException e) {   
  332.                     }   
  333.                 }   
  334.             }   
  335.             return answer;   
  336.         }   
  337.   
  338.   
  339.     private static void configure(ProviderManager pm) {   
  340.         // Service Discovery # Items   
  341.         pm.addIQProvider("query""http://jabber.org/protocol/disco#items"new DiscoverItemsProvider());  
  342.          // Service Discovery # Info   
  343.         pm.addIQProvider("query""http://jabber.org/protocol/disco#info"new DiscoverInfoProvider());  
  344.            
  345.         // Service Discovery # Items   
  346.         pm.addIQProvider("query""http://jabber.org/protocol/disco#items"new DiscoverItemsProvider());  
  347.          // Service Discovery # Info   
  348.         pm.addIQProvider("query""http://jabber.org/protocol/disco#info"new DiscoverInfoProvider());  
  349.            
  350.         //Offline Message Requests   
  351.         pm.addIQProvider("offline","http://jabber.org/protocol/offline"new OfflineMessageRequest.Provider());  
  352.                 //Offline Message Indicator   
  353.                 pm.addExtensionProvider("offline","http://jabber.org/protocol/offline"new OfflineMessageInfo.Provider());         
  354.                    
  355.         //vCard   
  356.         pm.addIQProvider("vCard","vcard-temp"new VCardProvider());   
  357.   
  358.         //FileTransfer   
  359.         pm.addIQProvider("si","http://jabber.org/protocol/si"new StreamInitiationProvider());  
  360.                 pm.addIQProvider("query","http://jabber.org/protocol/bytestreams"new BytestreamsProvider());  
  361.                 pm.addIQProvider("open","http://jabber.org/protocol/ibb"new IBBProviders.Open());  
  362.                 pm.addIQProvider("close","http://jabber.org/protocol/ibb"new IBBProviders.Close());  
  363.                 pm.addExtensionProvider("data","http://jabber.org/protocol/ibb"new IBBProviders.Data());          
  364.                 //Data Forms   
  365.             pm.addExtensionProvider("x""jabber:x:data"new DataFormProvider());   
  366.             //Html   
  367.             pm.addExtensionProvider("html""http://jabber.org/protocol/xhtml-im"new XHTMLExtensionProvider());  
  368.             //Ad-Hoc Command   
  369.             pm.addIQProvider("command""http://jabber.org/protocol/commands"new AdHocCommandDataProvider());          
  370.         // Chat State   
  371.         ChatStateExtension.Provider chatState = new ChatStateExtension.Provider();   
  372.         pm.addExtensionProvider("active""http://jabber.org/protocol/chatstates", chatState);  
  373.         pm.addExtensionProvider("composing""http://jabber.org/protocol/chatstates",   
  374.             chatState);   
  375.         pm.addExtensionProvider("paused""http://jabber.org/protocol/chatstates", chatState);  
  376.         pm.addExtensionProvider("inactive""http://jabber.org/protocol/chatstates", chatState);  
  377.         pm.addExtensionProvider("gone""http://jabber.org/protocol/chatstates", chatState);  
  378.         //MUC User,Admin,Owner   
  379.         pm.addExtensionProvider("x""http://jabber.org/protocol/muc#user"new MUCUserProvider());  
  380.         pm.addIQProvider("query""http://jabber.org/protocol/muc#admin"new MUCAdminProvider());  
  381.         pm.addIQProvider("query""http://jabber.org/protocol/muc#owner"new MUCOwnerProvider());  
  382.     }   
  383.   
  384.     private static void initFeatures(XMPPConnection xmppConnection) {   
  385.         ServiceDiscoveryManager.setIdentityName("Android_IM");   
  386.         ServiceDiscoveryManager.setIdentityType("phone");   
  387.         ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(xmppConnection);  
  388.          if(sdm == null) {   
  389.         sdm = new ServiceDiscoveryManager(xmppConnection);   
  390.         }   
  391.         sdm.addFeature("http://jabber.org/protocol/disco#info");   
  392.         sdm.addFeature("http://jabber.org/protocol/caps");   
  393.         sdm.addFeature("urn:xmpp:avatar:metadata");   
  394.         sdm.addFeature("urn:xmpp:avatar:metadata+notify");   
  395.         sdm.addFeature("urn:xmpp:avatar:data");   
  396.         sdm.addFeature("http://jabber.org/protocol/nick");   
  397.         sdm.addFeature("http://jabber.org/protocol/nick+notify");   
  398.         sdm.addFeature("http://jabber.org/protocol/xhtml-im");   
  399.         sdm.addFeature("http://jabber.org/protocol/muc");   
  400.         sdm.addFeature("http://jabber.org/protocol/commands");   
  401.         sdm.addFeature("http://jabber.org/protocol/si/profile/file-transfer");   
  402.         sdm.addFeature("http://jabber.org/protocol/si");   
  403.         sdm.addFeature("http://jabber.org/protocol/bytestreams");   
  404.         sdm.addFeature("http://jabber.org/protocol/ibb");   
  405.         sdm.addFeature("http://jabber.org/protocol/feature-neg");   
  406.         sdm.addFeature("jabber:iq:privacy");   
  407.     }   
  408.   
  409. }   


 

 

上面有兩張Spark客戶端的聊天室列表佔有者一列不同的原因:當使用以下程式碼獲取時不能獲取occupantsCount和subject的值:   

[java] view plaincopy
  1. System.out.println(roomInfo.getOccupantsCount() + " : " + roomInfo.getSubject());  

這是由於Openfire端有個bug(暫且這樣說吧,我不知為什麼Openfire這樣做).首先修改Smack的一個bug,修改RoomInfo類的RoomInfo(DiscoverInfo info) 方法:          

[java] view plaincopy
  1. Iterator<String> values = form.getField("muc#roominfo_subject").getValues();  
  2. if (values.hasNext()) {  
  3.     this.subject = values.next();  
  4. }  
  5. else {  
  6.     this.subject = "";  
  7. }  

 

改為:    

[java] view plaincopy
  1. final FormField subjField = form.getField("muc#roominfo_subject");   
  2. this.subject = subjField == null ? "" : subjField.getValues().next();  

  修改Openfire的原始碼,org/jivesoftware/openfire/muc/spi/MultiUserChatServiceImpl類的public DataForm getExtendedInfo(String name, String node, JID senderJID) 方法:          

        

[java] view plaincopy
  1.            /*final FormField fieldOcc = dataForm.addField(); */  
  2.            fieldSubj.setVariable("muc#roominfo_occupants");  
  3.            fieldSubj.setLabel(LocaleUtils.getLocalizedString("muc.extended.info.occupants"));  
  4.            fieldSubj.addValue(Integer.toString(room.getOccupantsCount()));  
  5.    

改為:          

         

[java] view plaincopy
  1. final FormField fieldOccu= dataForm.addField();  
  2. fieldOccu.setVariable("muc#roominfo_occupants");  
  3. fieldOccu.setLabel(LocaleUtils.getLocalizedString("muc.extended.info.occupants"));  
  4. fieldOccu.addValue(Integer.toString(room.getOccupantsCount()));   



 

使用asmack開發基於Android的IM系統同理.

相關文章