package com.xzjmyk.pm.activity.xmpp;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xzjmyk.pm.activity.MyApplication;
import com.xzjmyk.pm.activity.bean.LoginAuto;
import com.xzjmyk.pm.activity.helper.LoginHelper;
import com.xzjmyk.pm.activity.sp.UserSp;
import com.xzjmyk.pm.activity.ui.erp.util.LogUtil;
import com.xzjmyk.pm.activity.util.DeviceInfoUtil;
import com.xzjmyk.pm.activity.volley.Result;
import org.jivesoftware.smack.AbstractConnectionListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException.StreamErrorException;
import org.jivesoftware.smack.packet.StreamError;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.util.StringUtils;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Random;
public class XReconnectionManager extends AbstractConnectionListener {
// Holds the connection to the server
private XMPPConnection mConnection;
private boolean isReconnectionAllowed = false;
private Thread mReconnectionThread;
boolean mIsNetWorkActive;
// Holds the state of the reconnection
boolean doReconnecting = false;
Context mContext;
public XReconnectionManager(Context context, XMPPConnection connection, boolean reconnectionAllowed, boolean isNetWorkActive) {
mContext = context;
mConnection = connection;
mConnection.addConnectionListener(this);
isReconnectionAllowed = reconnectionAllowed;
mIsNetWorkActive = isNetWorkActive;
}
/**
* Returns true if the reconnection mechanism is enabled.
*
* @return true if automatic reconnections are allowed.
*/
private boolean isReconnectionAllowed() {
return doReconnecting && mIsNetWorkActive && !mConnection.isConnected() && isReconnectionAllowed;
}
public void setNetWorkState(boolean isNetWorkActive) {
mIsNetWorkActive = isNetWorkActive;
if (mIsNetWorkActive) {// 网络状态变为可用
LogUtil.d("Xmpp","isReconnectionAllowed:"+isReconnectionAllowed()
+" user:"+mConnection.getUser());
if (isReconnectionAllowed()) {
reconnect();
}
} else {
if (mReconnectionThread != null && mReconnectionThread.isAlive()) {
mReconnectionThread.interrupt();
}
}
}
/**
* Starts a reconnection mechanism if it was configured to do that. The algorithm is been executed when the first connection error is detected.
*
* The reconnection mechanism will try to reconnect periodically in this way:
*
* - First it will try 6 times every 10 seconds.
*
- Then it will try 10 times every 1 minute.
*
- Finally it will try indefinitely every 5 minutes.
*
*/
private synchronized void reconnect() {
if (this.isReconnectionAllowed()) {
// Since there is no thread running, creates a new one to attempt
// the reconnection.
// avoid to run duplicated reconnectionThread -- fd: 16/09/2010
if (mReconnectionThread != null && mReconnectionThread.isAlive()){
LogUtil.d("Xmpp","mReconnectionThread 线程已存在");
return;
}else{
LogUtil.d("Xmpp","mReconnectionThread 新线程:"+mConnection.getUser());
}
mReconnectionThread = new Thread() {
private int mRandomBase = new Random().nextInt(11) + 5; // between 5 and 15 seconds
/**
* Holds the current number of reconnection attempts
*/
private int attempts = 0;
/**
* Returns the number of seconds until the next reconnection attempt.
*
* @return the number of seconds until the next reconnection attempt.
*/
private int timeDelay() {
attempts++;
if (attempts > 13) {
return mRandomBase * 6 * 5; // between 2.5 and 7.5
// minutes (~5 minutes)
}
if (attempts > 7) {
return mRandomBase * 6; // between 30 and 90 seconds (~1
// minutes)
}
return mRandomBase; // 10 seconds
}
/**
* The process will try the reconnection until the connection succeed or the user cancel it
*/
public void run() {
// 重新连接之前,先检查Token状态
int checkTokenStatus = 0;
while (isReconnectionAllowed() && checkTokenStatus == 0) {
checkTokenStatus = syncCheckToken();
if (checkTokenStatus == 0) {// 表示检查失败,继续循环检查
LogUtil.d("Token","0:表示检查失败,继续循环检查");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else if (checkTokenStatus == 1) {// 表示检查成功Token过期(或出现不能继续请求Token状态的异常),停止重新登陆
doReconnecting = false;
LogUtil.d("Token","1:表示检查成功Token过期");
//TODO 关闭自动登录 可能出现账号异常情况
//mConnection.login();
// conflict();
} else if (checkTokenStatus == 2) {// 2、表示检查成功,Token没有改变,可以继续下面的重新登陆
LogUtil.d("Token","2:表示检查成功,Token没有改变,可以继续下面的重新登陆");
break;
}
}
while (isReconnectionAllowed()) {
// Makes a reconnection attempt
try {
if (isReconnectionAllowed()) {
mConnection.connect();
}
} catch (Exception e) {
notifyReconnectionFailed(e);// Fires the failed reconnection notification
}
int remainingSeconds = timeDelay();
while (isReconnectionAllowed() && remainingSeconds > 0) {
try {
Thread.sleep(1000);
remainingSeconds--;
notifyAttemptToReconnectIn(remainingSeconds);
} catch (InterruptedException e1) {
// Notify the reconnection has failed
notifyReconnectionFailed(e1);
}
}
}
}
};
mReconnectionThread.setName("Smack XReconnectionManager");
mReconnectionThread.setDaemon(true);
mReconnectionThread.start();
}
}
/**
* 在检查Token的时候,发现冲突了
*/
private void conflict() {
((CoreService) mContext).logout();
LoginHelper.broadcastConflict(mContext);
}
/**
* 检查Token状态,并返回int值
* 0、表示检查失败,继续循环检查
* 1、表示检查成功Token过期(或出现不能继续请求Token状态的异常),停止重新登陆
* 2、表示检查成功,Token没有改变,可以继续下面的重新登陆
*
* @return
*/
private int syncCheckToken() {// 同步网络请求Token
if (CoreService.DEBUG) {
Log.d(CoreService.TAG, "开始重新登陆前的 Token 状态检查");
String requestUrl = MyApplication.getInstance().getConfig().USER_LOGIN_AUTO;
if (requestUrl == null) {
return 1;
}
HttpURLConnection httpConn = null;
DataOutputStream out = null;
InputStream is = null;
try {
URL url = new URL(requestUrl);
httpConn = (HttpURLConnection) url.openConnection();
httpConn.setDoOutput(true);
httpConn.setDoInput(true);
httpConn.setConnectTimeout(5 * 1000);
httpConn.setReadTimeout(5 * 1000);
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
out = new DataOutputStream(httpConn.getOutputStream());
// 参数
String access_token = MyApplication.getInstance().mAccessToken;
if (TextUtils.isEmpty(access_token)) {
access_token = UserSp.getInstance(mContext).getAccessToken(null);
}
if (TextUtils.isEmpty(access_token)) {
return 1;
}
String serial = DeviceInfoUtil.getDeviceId(mContext);
if (TextUtils.isEmpty(serial)) {
return 1;
}
if (CoreService.DEBUG) {
Log.d(CoreService.TAG, "requestUrl:" + requestUrl);
Log.d(CoreService.TAG, "access_token:" + access_token);
Log.d(CoreService.TAG, "serial:" + serial);
}
StringBuilder sb = new StringBuilder();
sb.append("access_token=" + access_token + "&");
//String user = ((XMPPTCPConnection) mConnection).getDirectUser();
LogUtil.d("wang",JSON.toJSONString(mConnection));
String user =mConnection.getUser();
Log.d("wang", "user..." + user);
if (user == null) {
return 1;
}
sb.append("userId=" + StringUtils.parseName(user) + "&");
sb.append("serial=" + serial);
out.write(sb.toString().getBytes("UTF-8"));
out.flush();
int statusCode = httpConn.getResponseCode();
if (statusCode != 200) {
return 0;
}
is = httpConn.getInputStream();
if (is == null) {
return 0;
}
StringBuffer buffer = new StringBuffer();
int len = -1;
byte[] data = new byte[1024];
try {
while ((len = is.read(data)) != -1) {
buffer.append(new String(data, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
}
String result = buffer.toString();
if (CoreService.DEBUG) {
Log.d(CoreService.TAG, "检查状态result:" + result);
}
if (TextUtils.isEmpty(result)) {
Log.d("wang", "result==null");
return 0;
}
try {
JSONObject jsonObject = JSON.parseObject(result);
int resultCode = jsonObject.getIntValue(Result.RESULT_CODE);
if (resultCode != 1) {
return 0;
}
LoginAuto loginAuto = JSON.parseObject(jsonObject.getString(Result.DATA), LoginAuto.class);
LogUtil.d("Token","loginAuto:"+JSON.toJSONString(loginAuto));
if (loginAuto != null) {// 判断时候要继续重新登陆
int tokenExists = loginAuto.getTokenExists();// 1=令牌存在、0=令牌不存在
int serialStatus = loginAuto.getSerialStatus();// 1=没有设备号、2=设备号一致、3=设备号不一致
if (serialStatus == 2) {// 设备号一致,说明没有切换过设备
if (tokenExists == 1) {// Token存在,
return 2;
} else {// Token 不存在
return 1;
}
} else {// 设备号不一致,那么就是切换过手机
return 1;
}
} else {
return 0;
}
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
if (httpConn != null) {
httpConn.disconnect();
}
}
}
return 0;
}
private void notifyReconnectionFailed(Exception exception) {
if (isReconnectionAllowed()) {
LogUtil.d("xmppLogs","notifyReconnectionFailed");
// for (ConnectionListener listener : mConnection.getConnectionListeners()) {
// listener.reconnectionFailed(exception);
// }
}
}
private void notifyAttemptToReconnectIn(int seconds) {
if (isReconnectionAllowed()) {
LogUtil.d("xmppLogs","notifyAttemptToReconnectIn");
// for (ConnectionListener listener : mConnection.getConnectionListeners()) {
// listener.reconnectingIn(seconds);
// }
}
}
void release() {
doReconnecting = false;
if (mReconnectionThread != null && mReconnectionThread.isAlive()) {
mReconnectionThread.interrupt();
}
}
@Override
public void connectionClosed() {
doReconnecting = false;
}
@Override
public void connectionClosedOnError(Exception e) {
LogUtil.d("reconnect","<> mConnection:"+((XMPPTCPConnection)mConnection).getUser()
+" isAuthenticated():"+mConnection.isAuthenticated());
doReconnecting = true;
if (e instanceof StreamErrorException) {// 有人重复登陆
StreamErrorException xmppEx = (StreamErrorException) e;
StreamError error = xmppEx.getStreamError();
String reason = error.getCode();
if ("conflict".equals(reason)) {// 发出下线通知
if (CoreService.DEBUG)
Log.d(CoreService.TAG, "异常断开,有另外设备登陆啦");
conflict();
doReconnecting = false;
return;
}
}
// 因为其他原因导致下线,那么就开始重连
if (this.isReconnectionAllowed()) {
if (CoreService.DEBUG)
Log.d(CoreService.TAG, "异常断开,开始重连");
this.reconnect();
}
}
public XMPPConnection getmConnection() {
return mConnection;
}
}