天天看點

商城客服功能-------環信即時通訊

商城中基本上都添加了客服的功能,是買家和買家更友善的溝通。下面介紹的第三方即時通訊是環信。在客服這一子產品中我們要做的就是,實作客服與買家之間的交流(包括發送文字,表情,圖檔,語音,文檔,地理位置等),發送訂單消息,商品資訊等,實作二者之間的互動,買家給客服打分。補充就是六天紀錄的删除,發送資訊的時間,接接收消息的提醒等。

1、新增賬號,并申請客服賬号在文檔裡做出了詳細的說明,位址:http://docs.easemob.com/cs/300visitoraccess/10nativeapp

2、建立應用,關聯app,

3、下載下傳sdk和環信的的ui架構(ui架構使用與否看自己)

4、這裡為了快速展示,就使用官方提供的ui架構。把下載下傳的sdk考入程式

5、環信的賬号注冊分為兩種,開放注冊和授權注冊。我們要選擇開放注冊才可以讓使用者自由的新增賬號。

6、下面就是整個程式最重要的部分了,聊天界面。

7、商品消息的發送-----繪制用戶端和服務端的軌迹消息。

8、訂單資訊的發送,同樣繪制兩端的軌迹消息。

9、客服邀請使用者打分

10、全局監聽,想要實作消息軌迹的繪制

11、語音

進入代碼開發:

1、首先建立一個單例類,用來初始化EaseUI,對EasaUI進行配置

empty

public class CustomHelper {
	private static CustomHelper customhelper;
	private String TAG = "CustomHelper";
	private Context appContext;
	private EaseUI easeUI;
	private EMConnectionListener connectionListener;
	/**
	 * EMEventListener
	 */
	protected EMEventListener eventListener = null;
	private boolean alreadyNotified = false;

	private CustomHelper() {

	}

	public synchronized static CustomHelper getInstance() {
		if (customhelper == null) {
			customhelper = new CustomHelper();
		}
		return customhelper;
	}

	public void init(Context context) {
		CustomConfigManager.init(context);

		EMChat.getInstance().setAppkey(CustomConfig.DEFALUT_APPKEY);

		// EMChat.getInstance().setAutoLogin(false);

		EaseUI.getInstance().init(context);

		if (EaseUI.getInstance().init(context)) {
			appContext = context;
			// 在小米手機上當app被kill時使用小米推送進行消息提示,SDK已支援,可選
			EMChatManager.getInstance().setMipushConfig("2882303761517370134",
					"5131737040134");
			// 設為調試模式,打成正式包時,最好設為false,以免消耗額外的資源
			// EMChat.getInstance().setDebugMode(true);
			// get easeui instance
			easeUI = EaseUI.getInstance();
			// 調用easeui的api設定providers
			setEaseUIProviders();
			// demoModel = new DemoModel(context);
			// 初始化PreferenceManager
			// PreferenceManager.init(context);
			// 設定全局監聽
			setGlobalListeners();
		

		}
	}

	/**
	 * 檢測是否為訂單消息或者為軌迹消息
	 * 
	 * @param message
	 * @return
	 */
	public boolean isPictureTxtMessage(EMMessage message) {
		JSONObject jsonObj = null;
		try {
			jsonObj = message
					.getJSONObjectAttribute(CustomConfig.MESSAGE_ATTR_MSGTYPE);
		} catch (EaseMobException e) {
		}
		if (jsonObj == null) {
			return false;
		}
		if (jsonObj.has("order") || jsonObj.has("track")) {
			return true;
		}
		return false;
	}

	/**
	 * 檢測是否為訂單消息或者為軌迹消息
	 * 
	 * @param message
	 * @return
	 */
	public boolean isTRACKTxtMessage(EMMessage message) {
		JSONObject jsonObj = null;
		try {
			jsonObj = message
					.getJSONObjectAttribute(CustomConfig.MESSAGE_ATTR_MSGTYPE);
		} catch (EaseMobException e) {
		}
		if (jsonObj == null) {
			return false;
		}
		if (jsonObj.has("track")) {
			return true;
		}
		return false;
	}

	/**
	 * 檢測是否為訂單消息或者為軌迹消息
	 * 
	 * @param message
	 * @return
	 */
	public boolean isORDERTxtMessage(EMMessage message) {
		JSONObject jsonObj = null;
		try {
			jsonObj = message
					.getJSONObjectAttribute(CustomConfig.MESSAGE_ATTR_MSGTYPE);
		} catch (EaseMobException e) {
		}
		if (jsonObj == null) {
			return false;
		}
		if (jsonObj.has("order")) {
			return true;
		}
		return false;
	}

	// //設定頭次昂
	protected void setEaseUIProviders() {
	

		// 不設定,則使用easeui預設的
		easeUI.getNotifier()
				.setNotificationInfoProvider(
						new com.easemob.easeui.model.EaseNotifier.EaseNotificationInfoProvider() {

							public String getTitle(EMMessage message) {
								// 修改标題,這裡使用預設
								return null;
							}

							@Override
							public int getSmallIcon(EMMessage message) {
								// 設定小圖示,這裡為預設
								return 0;
							}

							// @Override
							// public String getDisplayedText(EMMessage message)
							// {
							// // 設定狀态欄的消息提示,可以根據message的類型做相應提示
							// String ticker =
							// EaseCommonUtils.getMessageDigest(message,
							// appContext);
							// if (message.getType() == Type.TXT) {
							// ticker = ticker.replaceAll("\\[.{2,3}\\]",
							// "[表情]");
							// }
							// return message.getFrom() + ": " + ticker;
							// }

							// public String getLatestText(EMMessage message,
							// int fromUsersNum, int
							// messageNum) {
							// return null;
							// // return fromUsersNum + "個基友,發來了" + messageNum +
							// "條消息";
							// }
							@Override
							public String getDisplayedText(EMMessage message) {
								// 設定狀态欄的消息提示,可以根據message的類型做相應提示
								String ticker = EaseCommonUtils
										.getMessageDigest(message, appContext);
								if (message.getType() == EMMessage.Type.TXT) {
									ticker = ticker.replaceAll("\\[.{2,3}\\]",
											"[表情]");
								}
								if (message.getFrom().equals(
										MLSPUtil.get(appContext,
												CustomConfig.DEFALUT_IM, ""))) {
									return "具商客服: " + ticker;
								} else {
									return message.getFrom() + ": " + ticker;
								}
							}

							/**
							 * 根據消息條數來判斷如果顯示
							 * 
							 * @param message
							 *            接收到的消息
							 * @param fromUsersNum
							 *            發送人的數量
							 * @param messageNum
							 *            消息數量
							 * @return
							 */
							@Override
							public String getLatestText(EMMessage message,
									int fromUsersNum, int messageNum) {
								// 當隻有一個人,發來一條消息時,顯示消息内容 TODO 表情符顯示為圖檔
								if (fromUsersNum == 1 && messageNum == 1) {
									return "具商客服:"
											+ EaseCommonUtils.getMessageDigest(
													message, appContext)
													.replace("\\[.{2,3}\\]",
															"[表情]");
								} else {
									
									
									return "具商發來 " + messageNum + " 條消息";
								}
							}

							@Override
							public Intent getLaunchIntent(EMMessage message) {
								
								//聯系客服
//						    	if (TextUtils.isEmpty(CustomConfigManager.getInstance().getCurrentIM())) {
						    		
						    	String	tochatUserName = CustomConfig.DEFALUT_IM;
						    	
//						    	} else {
//						    		tochatUserName = CustomConfigManager.getInstance().getCurrentIM();
//						    		
//						    	}
								// 設定點選通知欄跳轉事件
								Intent intent = new Intent(appContext,
										ChatActivity.class);
								ChatType chatType = message.getChatType();
								if (chatType == ChatType.Chat) { // 單聊資訊
									intent.putExtra(EaseConstant.EXTRA_USER_ID,
											message.getFrom());
									intent.putExtra(
											EaseConstant.EXTRA_CHAT_TYPE,
											Constant.CHATTYPE_SINGLE);
									// intent.putExtra(EaseConstant.EXTRA_SHOW_USERNICK,
									// true);
								}
								return intent;
							}
						});

		// 設定表情provider
		easeUI.setEmojiconInfoProvider(new EaseUI.EaseEmojiconInfoProvider() {
			@Override
			public EaseEmojicon getEmojiconInfo(String emojiconIdentityCode) {
				EaseEmojiconGroupEntity data = EmojiconExampleGroupData
						.getData();
				for (EaseEmojicon emojicon : data.getEmojiconList()) {
					if (emojicon.getIdentityCode().equals(emojiconIdentityCode)) {
						return emojicon;
					}
				}
				return null;
			}

			@Override
			public Map
   
     getTextEmojiconMapping() {
				// 傳回文字表情emoji文本和圖檔(resource id或者本地路徑)的映射map
				return null;
			}
		});

	}

	/**
	 * 判斷是否為滿意度調查類型的消息
	 * 
	 * @param message
	 * @return
	 */
	public boolean isCtrlTypeMessage(EMMessage message) {
		JSONObject jsonObj = null;
		try {
			jsonObj = message
					.getJSONObjectAttribute(CustomConfig.C_ATTR_KEY_WEICHAT);
			if (jsonObj.has(CustomConfig.C_ATTR_CTRLTYPE)) {
				String type = jsonObj.getString(CustomConfig.C_ATTR_CTRLTYPE);
				if (type.equalsIgnoreCase(CustomConfig.C_ATTR_INVITEENQUIRY)
						|| type.equalsIgnoreCase(CustomConfig.C_ATTR_ENQUIRY)) {
					return true;
				}
			}
		} catch (EaseMobException e) {
			e.printStackTrace();
		} catch (JSONException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * 判斷是否為使用者軌迹類型的消息
	 * 
	 * @param message
	 * @return
	 */
	public boolean isTrackMessage(EMMessage message) {
		JSONObject jsonObj = null;
		try {
			jsonObj = message
					.getJSONObjectAttribute(CustomConfig.C_ATTR_KEY_MSGTYPE);
			if (jsonObj.has(CustomConfig.C_ATTR_TRACK)) {
				return true;
			}
		} catch (EaseMobException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * 判斷是否為訂單消息
	 * 
	 * @param message
	 * @return
	 */
	public boolean isOrderFormMessage(EMMessage message) {
		JSONObject jsonObj = null;
		try {
			jsonObj = message
					.getJSONObjectAttribute(CustomConfig.C_ATTR_KEY_MSGTYPE);
			if (jsonObj.has(CustomConfig.C_ATTR_ORDER)) {
				return true;
			}
		} catch (EaseMobException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * 設定全局事件監聽
	 */
	protected void setGlobalListeners() {
		// create the global connection listener
		connectionListener = new EMConnectionListener() {
			@Override
			public void onDisconnected(int error) {
				if (error == EMError.USER_REMOVED) {
					onCurrentAccountRemoved();

				} else if (error == EMError.CONNECTION_CONFLICT) {
					onConnectionConflict();

				}
			}

			@Override
			public void onConnected() {
				// in case group and contact were already synced, we supposed to
				// notify sdk we are ready to receive the events
				CustomHelper.getInstance().notifyForRecevingEvents();
			}
		};
		// 注冊連接配接監聽
		EMChatManager.getInstance().addConnectionListener(connectionListener);
		// 注冊消息事件監聽
		registerEventListener();
	}

	/**
	 * 賬号在别的裝置登入
	 */
	protected void onConnectionConflict() {
		Intent intent = new Intent(appContext, MainFragment.class);
		intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		intent.putExtra(Constant.ACCOUNT_CONFLICT, true);
		appContext.startActivity(intent);
		// popActivity(appContext);
		((Activity) appContext).finish();

	}

	/**
	 * 賬号被移除
	 */
	protected void onCurrentAccountRemoved() {
		Intent intent = new Intent(appContext, MainFragment.class);
		intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		intent.putExtra(Constant.ACCOUNT_REMOVED, true);
		appContext.startActivity(intent);
		((Activity) appContext).finish();

	}

	/**
	 * 全局事件監聽 因為可能會有UI頁面先處理到這個消息,是以一般如果UI頁面已經處理,這裡就不需要再次處理 activityList.size()
	 * <= 0 意味着所有頁面都已經在背景運作,或者已經離開Activity Stack
	 */
	protected void registerEventListener() {
		eventListener = new EMEventListener() {
			private BroadcastReceiver broadCastReceiver = null;

			@Override
			public void onEvent(EMNotifierEvent event) {
				EMMessage message = null;
				if (event.getData() instanceof EMMessage) {
					message = (EMMessage) event.getData();
					EMLog.d(TAG, "receive the event : " + event.getEvent()
							+ ",id : " + message.getMsgId());
				}

				switch (event.getEvent()) {
				case EventNewMessage:
					// 應用在背景,不需要重新整理UI,通知欄提示新消息
					if (!easeUI.hasForegroundActivies()) {
						getNotifier().onNewMsg(message);
					}
					break;
				case EventOfflineMessage:
					if (!easeUI.hasForegroundActivies()) {
						EMLog.d(TAG, "received offline messages");
						List
    
      messages = (List
     
      ) event
								.getData();
						getNotifier().onNewMesg(messages);
					}
					break;
				// below is just giving a example to show a cmd toast, the app
				// should not follow this
				// so be careful of this
				case EventNewCMDMessage: {

					EMLog.d(TAG, "收到透傳消息");
					// 擷取消息body
					CmdMessageBody cmdMsgBody = (CmdMessageBody) message
							.getBody();
					final String action = cmdMsgBody.action;// 擷取自定義action

					// 擷取擴充屬性 此處省略
					// message.getStringAttribute("");
					EMLog.d(TAG, String.format("透傳消息:action:%s,message:%s",
							action, message.toString()));
					final String str = appContext
							.getString(R.string.receive_the_passthrough);

					final String CMD_TOAST_BROADCAST = "easemob.demo.cmd.toast";
					IntentFilter cmdFilter = new IntentFilter(
							CMD_TOAST_BROADCAST);

					if (broadCastReceiver == null) {
						broadCastReceiver = new BroadcastReceiver() {

							@Override
							public void onReceive(Context context, Intent intent) {
								// TODO Auto-generated method stub
								Toast.makeText(appContext,
										intent.getStringExtra("cmd_value"),
										Toast.LENGTH_SHORT).show();
							}
						};

						// 注冊廣播接收者
						appContext.registerReceiver(broadCastReceiver,
								cmdFilter);
					}

					Intent broadcastIntent = new Intent(CMD_TOAST_BROADCAST);
					broadcastIntent.putExtra("cmd_value", str + action);
					appContext.sendBroadcast(broadcastIntent, null);

					break;
				}
				case EventDeliveryAck:
					message.setDelivered(true);
					break;
				case EventReadAck:
					message.setAcked(true);
					break;
				// add other events in case you are interested in
				default:
					break;
				}

			}
		};

		EMChatManager.getInstance().registerEventListener(eventListener);
	}

	/**
	 * 是否登入成功過
	 * 
	 * @return
	 */
	public boolean isLoggedIn() {
		return EMChat.getInstance().isLoggedIn();
	}

	/**
	 * 登出
	 * 
	 * @param unbindDeviceToken
	 *            是否解綁裝置token(使用GCM才有)
	 * @param callback
	 *            callback
	 */
	public void logout(boolean unbindDeviceToken, final EMCallBack callback) {
		EMChatManager.getInstance().logout(unbindDeviceToken, new EMCallBack() {

			@Override
			public void onSuccess() {
				if (callback != null) {
					callback.onSuccess();
				}

			}

			@Override
			public void onProgress(int progress, String status) {
				if (callback != null) {
					callback.onProgress(progress, status);
				}
			}

			@Override
			public void onError(int code, String error) {
				if (callback != null) {
					callback.onError(code, error);
				}
			}
		});
	}

	/**
	 * 擷取消息通知類
	 * 
	 * @return
	 */
	public EaseNotifier getNotifier() {
		return easeUI.getNotifier();
	}

	public synchronized void notifyForRecevingEvents() {
		if (alreadyNotified) {
			return;
		}

		// 通知sdk,UI 已經初始化完畢,注冊了相應的receiver和listener, 可以接受broadcast了
		EMChat.getInstance().setAppInited();
		alreadyNotified = true;
	}

}

     
    
   
           

2、接着就要在application做一些全局工作

商城客服功能-------環信即時通訊

3、最重要的就是要實作聊天和商品資訊的發送和軌迹繪制

 a、通過官方的文檔,我們可以看出,發送文本資訊要用

 b、商品的軌迹繪制,資訊需要包括商品名稱,價格,圖檔,連結,說明。消息展示則需要我們自己定義排版,在布局中的控件的id名稱則需要和EasaUI中定義的一樣,否則會出錯,親身踩坑。

empty
/**
	 * 監聽事件 父類
	 */
	  @Override
	    protected void setUpView() {
	        setChatFragmentHelper(this);   
	        super.setUpView();
	    }
	    
	   
	   /**
	   軌迹消息
	   **/
	   	private void sendPictureTxtMessage() {
		// //建立一條文本消息
		EMMessage message = EMMessage.createTxtSendMessage("商品消息已經發送到客服",
				toChatUsername);
		
		TrackMessageEntity msg=new TrackMessageEntity(25, channame, chanprice, "木床", chantu,"http://114.55.99.240/Commodity_Show.aspx?id="+chanid);
		//發送json擴充
//		JSONObject jsonMsgType = MessageHelper.getMessageExtFromPicture(witchCHAT);
		JSONObject jsonMsgType=msg.getJSONObject();
	
		if (jsonMsgType != null) {
			// 給消息設定擴充
			message.setAttribute("msgtype",jsonMsgType);
			  setUserAttibutes(message);
		        setSkillGroup(message);
			sendMessage(message);
		}
	} 
	   
	   
	    /**
     * ---------------------------------------------------
     * 設定自定義消息提供者
     *
     * @return
     */
    public EaseCustomChatRowProvider onSetCustomChatRowProvider() {
        return new CustomChatRowProvider();
    }

	/**
     * 自定義實作ChatRow提供者
     */
    class CustomChatRowProvider implements EaseCustomChatRowProvider {
        /**
         * 傳回自定義消息的個數(這個個數必須和你自定義的ChatRow界面個數一緻,否則會數組越界)
         * FATAL EXCEPTION: main Process: com.easemob.easeui.customer, PID: 32217
         * java.lang.ArrayIndexOutOfBoundsException: length=18; index=18
         * at android.widget.AbsListView$RecycleBin.addScrapView(AbsListView.java:6588)
         *
         * @return
         */
        public int getCustomChatRowTypeCount() {
            return 6;
        }

        /**
         * 傳回消息的類型
         *
         * @param message
         * @return
         */
        public int getCustomChatRowType(EMMessage message) {
        	Log.v("--88--", "buju ");
            if (message.getType() == EMMessage.Type.TXT) {
                if (CustomHelper.getInstance().isCtrlTypeMessage(message)) {
                    return message.direct == EMMessage.Direct.RECEIVE ? 2 : 1;
                } 
                else if (CustomHelper.getInstance().isTrackMessage(message)) {
                    return message.direct == EMMessage.Direct.RECEIVE ? 4 : 3;
                } 
            	else if (CustomHelper.getInstance().isOrderFormMessage(message)) {
                    return message.direct == EMMessage.Direct.RECEIVE ? 6 : 5;
                }
            }
            return 0;
        }

        /**
         * 傳回自定義消息的實作
         *
         * @param message
         * @param position
         * @param adapter
         * @return
         */
        public EaseChatRow getCustomChatRow(EMMessage message, int position, BaseAdapter adapter) {
            if (message.getType() == EMMessage.Type.TXT) {
                if (CustomHelper.getInstance().isCtrlTypeMessage(message)) {
                    CtrlTypeChatRow ctrlTypeChatRow = new CtrlTypeChatRow(mActivity, message, position, adapter);
                    ctrlTypeChatRow.setmChatRowListener(new MyChatRowListener());
                    return ctrlTypeChatRow;
                } 
                else if (CustomHelper.getInstance().isTrackMessage(message)) {
                    return new TrackChatRow(getActivity(), message, position, adapter);
                } 
                else if (CustomHelper.getInstance().isOrderFormMessage(message)) {
//                    return new OrderChatRow(mActivity, message, position, adapter);
                }
            }
            return null;
        }
    }

	@Override
	public void onMessageBubbleLongClick(EMMessage message) {
		// TODO Auto-generated method stub
		
	}
	/**
     * ------------------------------------------------------------------
     * 


     * 滿意度評價的回調接口,為了實作用戶端在滿意度ChatRow中發送滿意度類型的消息
     */
    class MyChatRowListener implements CtrlTypeChatRow.CustomerChatRowListener {

        @Override
        public void onChatRowInteraction(EnquiryEntity enquiryEntity) {
            EMMessage message = EMMessage.createTxtSendMessage("客服圖文混排消息", toChatUsername);
            JSONObject jsonWeiChat = new JSONObject();
            JSONObject jsonCtrlArgs = new JSONObject();
            try {
                jsonCtrlArgs.put("inviteId", enquiryEntity.getInviteId());
                jsonCtrlArgs.put("serviceSessionId", enquiryEntity.getServiceSessionId());
                jsonCtrlArgs.put("detail", enquiryEntity.getDetail());
                jsonCtrlArgs.put("summary", enquiryEntity.getSummary());

                jsonWeiChat.put("ctrlType", "enquiry");
                jsonWeiChat.put("ctrlArgs", jsonCtrlArgs);
            } catch (JSONException e) {
                e.printStackTrace();
            }
            message.setAttribute(CustomConfig.C_ATTR_KEY_WEICHAT, jsonWeiChat);
            sendMessage(message);
        }
    } 	
           

c、判斷客服消息的唯獨數量:

EMConversation conversation = EMChatManager.getInstance().getConversation(CustomConfig.DEFALUT_IM);

int chatnumint=conversation.getUnreadMsgCount();

環信客服的介入看似複雜,其實并不複雜

附:1、環信客服內建的ui架構,es上使用ui   http://download.csdn.net/detail/heiya0409/9711950

2、在as上不使用官方ui,自己布局的bao   http://download.csdn.net/detail/heiya0409/9711958