⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 messagehelper.java

📁 java写的qq代码实现qq的部分功能
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
/*
* LumaQQ - Java QQ Client
*
* Copyright (C) 2004 luma <stubma@163.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package edu.tsinghua.lumaqq.ui.helper;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.swt.graphics.Image;

import edu.tsinghua.lumaqq.IconHolder;
import edu.tsinghua.lumaqq.LumaQQ;
import edu.tsinghua.lumaqq.MessageQueue;
import edu.tsinghua.lumaqq.SoundDaemon;
import edu.tsinghua.lumaqq.models.ClusterModel;
import edu.tsinghua.lumaqq.models.FriendModel;
import edu.tsinghua.lumaqq.models.GroupModel;
import edu.tsinghua.lumaqq.models.IQQNode;
import edu.tsinghua.lumaqq.qq.QQ;
import edu.tsinghua.lumaqq.qq.QQClient;
import edu.tsinghua.lumaqq.qq.Util;
import edu.tsinghua.lumaqq.qq.beans.ClusterIM;
import edu.tsinghua.lumaqq.qq.beans.NormalIM;
import edu.tsinghua.lumaqq.qq.packets.in.ReceiveIMPacket;
import edu.tsinghua.lumaqq.qq.packets.in.SystemNotificationPacket;
import edu.tsinghua.lumaqq.ui.MainShell;
import edu.tsinghua.lumaqq.ui.ReceiveIMWindow;
import edu.tsinghua.lumaqq.ui.SendClusterIMWindow;
import edu.tsinghua.lumaqq.ui.SendIMWindow;
import edu.tsinghua.lumaqq.ui.tool.HeadFactory;
import edu.tsinghua.lumaqq.ui.tool.ShellFactory;
import edu.tsinghua.lumaqq.ui.tool.ShellRegistry;
import edu.tsinghua.lumaqq.utils.OptionUtil;
import edu.tsinghua.lumaqq.utils.ReplyUtil;
import edu.tsinghua.lumaqq.widgets.rich.IRichContent;
import edu.tsinghua.swt.models.ShutterModel;
import edu.tsinghua.swt.widgets.CoolButton;
import edu.tsinghua.swt.widgets.Shutter;

/**
 * 消息处理帮助类
 * 
 * @author luma
 */
public class MessageHelper {
    private static Log log = LogFactory.getLog(MessageHelper.class);
    
    private MainShell main;
    private IconHolder icons;
    
	// 分片缓冲,有的长消息会变成几个分片发送,需要保存起来等待所有分片完成
    // key是消息id,value是个Object数组,保存了消息的分片
	private Map fragmentCache; 
    
    public MessageHelper(MainShell main) {
        this.main = main;
        icons = IconHolder.getInstance();
        fragmentCache = new HashMap();
    }    
    
    /**
     * 把字节数组转换为String,它为我们处理缺省表情的问题
     * 
     * @param b
     * 		消息字节数组
     * @return
     * 		String
     */
    public String convertBytes(byte[] b) {
        StringBuffer sb = new StringBuffer();
        int offset = 0;
        int length = 0;
        for(int i = 0; i < b.length; i++) {
            if(b[i] == QQ.QQ_DEFAULT_FACE_TAG) {
                sb.append(Util.getString(b, offset, length));                
                sb.append((char)b[i]).append((char)(b[i + 1] & 0xFF));
                i++;
                offset = i + 1;
                length = 0;
            } else
                length++;
        }
        if(length > 0)
            sb.append(Util.getString(b, offset, length));
        return sb.toString();
    }
    
    /**
     * 检查这个消息是完整消息还是分片
     * 
     * @return
     * 		true表示这个消息是分片消息
     */
    private boolean isFragment(NormalIM im) {
        return im.totalFragments > 1;
    }
    
    /**
     * 检查这个消息是完整消息还是分片
     * 
     * @return
     * 		true表示这个消息是分片消息
     */
    private boolean isFragment(ClusterIM im) {
        return im.totalFragments > 1;
    }
    
    /**
     * 添加一个普通消息分片
     * 
     * @param im
     */
    private void addFragment(NormalIM im) {
        Integer id = new Integer(im.messageId);
        Object[] fragments = (Object[])fragmentCache.get(id);
        if(fragments == null || fragments.length != im.totalFragments) {
            fragments = new Object[im.totalFragments];
            fragmentCache.put(id, fragments);
        }
        fragments[im.fragmentSequence] = im;
    }
    
    /**
     * 添加一个群消息分片
     * 
     * @param im
     */
    private void addFragment(ClusterIM im) {
        Integer id = new Integer(im.messageId);
        Object[] fragments = (Object[])fragmentCache.get(id);
        if(fragments == null || fragments.length != im.totalFragments) {
            fragments = new Object[im.totalFragments];
            fragmentCache.put(id, fragments);
        }
        fragments[im.fragmentSequence] = im;
    }
    
    /**
     * 得到完整的消息,同时把这个消息从分片缓冲中清楚。调用此方法前,必须先用
     * isMessageComplete()判断分片是否都已经收到
     * 
     * @param messageId
     * 		消息id
     * @return
     * 		ClusterIM对象
     */
    private ClusterIM getIntegratedClusterIM(char messageId) {
        Integer id = new Integer(messageId);
        Object[] fragments = (Object[])fragmentCache.remove(id);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        for(int i = 0; i < fragments.length; i++) {
            try {
                baos.write(((ClusterIM)fragments[i]).messageBytes);
            } catch (IOException e) {
            }
        }        
        ClusterIM ret = (ClusterIM)fragments[fragments.length - 1];
    	ret.message = convertBytes(baos.toByteArray());
        return ret;
    }
    
    /**
     * 得到完整的普通消息
     * 
     * @param messageId
     * 		消息ID
     * @return
     * 		NormalIM对象
     */
    private NormalIM getIntegratedNormalIM(char messageId) {
        Integer id = new Integer(messageId);
        Object[] fragments = (Object[])fragmentCache.remove(id);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        for(int i = 0; i < fragments.length; i++) {
            try {
                baos.write(((NormalIM)fragments[i]).messageBytes);
            } catch (IOException e) {
            }
        }        
        NormalIM ret = (NormalIM)fragments[0];
    	ret.message = convertBytes(baos.toByteArray());
        return ret;
    }
    
    /**
     * 检查是否一个长消息的分片都已经收到了
     * 
     * @param messageId
     * 		消息id
     * @return
     * 		true表示已经收到
     */
    private boolean isMessageComplete(char messageId) {      
        Integer id = new Integer(messageId);
        if(!fragmentCache.containsKey(id))
            return false;
        
        Object[] fragments = (Object[])fragmentCache.get(id);
        for(int i = 0; i < fragments.length; i++) {
            if(fragments[i] == null)
                return false;
        }
        return true;
    }
	
	/**
	 * 推入一个群通知消息,一个群可以有很多种类型的消息,一般来说,除了普通群消息之外的
	 * 其他消息,我们都看做是一个群通知,也就是要闪那个小喇叭,而不是把他显示在发送消息
	 * 的窗口中。这样的群通知消息有申请加入群,退出群,同意加入群等等等等。判断的逻辑基
	 * 本上也和系统消息差不多,先看小喇叭闪没闪,没闪就闪他,再看是不是第一条消息,是就
	 * 再闪tray icon
	 * 
	 * @param packet
	 * 			消息包
	 */
	public void putClusterNotification(ReceiveIMPacket packet) {
		// 保存到聊天记录,群通知消息部分有些特殊(和系统消息一个意思)
		//     分成两个域,第一个是消息类型字节,第二个是消息内容,用|分隔,这只
		//     是个权宜之计,因为我匆匆定义的聊天记录格式比较土
		StringBuffer sb = new StringBuffer();
		String sender = String.valueOf(packet.sender);
		String clusterId = String.valueOf(packet.header.sender);
		sb.append((int)packet.header.type);
		sb.append('|');
		if(packet.header.type == QQ.QQ_RECV_IM_ADDED_TO_CLUSTER || packet.header.type == QQ.QQ_RECV_IM_CREATE_CLUSTER) {
			if(packet.clusterType == QQ.QQ_CLUSTER_TYPE_PERMANENT)
				sb.append(LumaQQ.getString("cluster.message.permanent.cluster.created", new Object[] { clusterId, sender }));
			else
				sb.append(LumaQQ.getString("cluster.message.temporary.cluster.created", new Object[] { clusterId, sender }));				
		} else if(packet.header.type == QQ.QQ_RECV_IM_DELETED_FROM_CLUSTER) {
		    if(packet.sender == main.getMyModel().getQQ())
		        sb.append(LumaQQ.getString("cluster.message.removed", new Object[] { sender }));
		    else
		        sb.append(LumaQQ.getString("cluster.message.exit", new Object[] { sender, clusterId } ));
		}
		else if(packet.header.type == QQ.QQ_RECV_IM_REQUEST_JOIN_CLUSTER) 
			sb.append(LumaQQ.getString("cluster.message.request", new Object[] { sender, clusterId, packet.message }));
		else if(packet.header.type == QQ.QQ_RECV_IM_APPROVE_JOIN_CLUSTER) 
			sb.append(LumaQQ.getString("cluster.message.approved", new Object[] { sender }));
		else if(packet.header.type == QQ.QQ_RECV_IM_REJECT_JOIN_CLUSTER)
			sb.append(LumaQQ.getString("cluster.message.rejected", new Object[] { sender, packet.message }));
		main.getMessageManager().saveMessage(10000, packet.header.sender, packet.sender, sb.toString(), (int)(System.currentTimeMillis() / 1000));
		// 调整动画状态
		if(!main.getMessageQueue().hasNext())
		    main.getUIHelper().startBlinkImage(icons.getImage(IconHolder.icoSysMsg));
		if(!main.isSystemMessageIconBlinking())
			main.startBlinkSystemMessageIcon();
		
		main.getMessageQueue().putSystemMessage(packet);
		// 播放声音
		main.getSoundDaemon().play(LumaQQ.SYS_MSG_SOUND);
		log.debug("一个系统消息被推入队列");
	}
	
	/**
	 * 处理延迟队列中的消息
	 */
	public void processPostponedIM() {
		ReceiveIMPacket packet;
		while((packet = (ReceiveIMPacket)main.getMessageQueue().getPostponedMessage()) != null) { 
			log.debug("发现一条延迟消息,处理之");
			if(packet.header.type == QQ.QQ_RECV_IM_CLUSTER_IM)
				putClusterIM(packet);
			else 
				putNormalIM(packet);
		}
	}
	 
	/**
	 * 推入一条消息并更新各种图标的闪烁状态,这个方法会检查是否好友列表已经
	 * 得到,如果没有,推入延迟队列
	 * 
	 * @param packet
	 * 			消息包
	 */
	public void putNormalIM(ReceiveIMPacket packet) {
	    // 如果这个消息是分片消息,如果这个消息已经完成,则继续处理,否则推入分片缓冲
	    if(isFragment(packet.normalIM)) {
	        addFragment(packet.normalIM);
	        if(isMessageComplete(packet.normalIM.messageId)) {
	            packet.normalIM = getIntegratedNormalIM(packet.normalIM.messageId);
	        } else {
	            return;
	        }
	    } else {
	        packet.normalIM.message = convertBytes(packet.normalIM.messageBytes);
	    }
	    
		// 得到好友在model中的位置,但是有可能为null,因为也许这是用户的第一次登陆
		//     其好友列表还没得到,但是这时候有消息来了,所有也无法闪烁图标了,对于
		//     这种情况,需要特殊处理一下,基本的方法是把消息推入延迟处理队列
		int[] indices = main.getMVCHelper().getFriendCoordinate(packet.normalHeader.sender);
		if((indices == null || indices.length == 0)) { 
		    if(!main.isFriendListFinished()) {
				main.getMessageQueue().postponeMessage(packet);
				log.debug("发来消息的好友还未出现在好友列表中,延迟处理该消息");		        
		    } else {
		        if(packet.header.type == QQ.QQ_RECV_IM_FROM_STRANGER)
		            log.debug("收到一个坏人来的消息,忽略");
		        else
		            putNormalIM(packet, null, true);
		    }
		} else {
		    if(packet.header.type == QQ.QQ_RECV_IM_FROM_STRANGER) {
	            GroupModel g = (GroupModel)main.getModel().getTab(indices[0]);
	            if(g.isBlackList())
	                log.debug("收到一个坏人来的消息,忽略");
	            else
	                putNormalIM(packet, indices, true);
		    } else
		        putNormalIM(packet, indices, false);
		}
	}
	
	/**
	 * 推入一条消息并更新各种图标闪烁状态,这个方法接收好友的索引参数
	 *  
	 * @param packet
	 * 				收到的消息包
	 * @param indices
	 * 				好友的组索引和组内偏移,如果为null,说明这个用户不是我的好友
	 */
	public void putNormalIM(ReceiveIMPacket packet, int[] indices, boolean isStranger) {
		// 保存到聊天记录中
		main.getMessageManager().saveMessage(packet.normalHeader.sender, packet.normalHeader.sender, packet.normalHeader.sender, packet.normalIM.message, (int)(packet.normalHeader.sendTime / 1000));

	    MVCHelper mvcHelper = main.getMVCHelper();
	    OptionUtil options = OptionUtil.getInstance();
	    ShutterModel model = main.getModel();
		
		// 如果设置了拒绝陌生人消息
		if(isStranger) {
		    if(options.isRejectStranger()) {
		        if(indices == null || indices.length == 0) {
				    log.debug("设置了拒绝陌生人消息,忽略该消息");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -