package com.tomclaw.mandarin.im.icq;
import android.text.TextUtils;
import com.tomclaw.mandarin.R;
import com.tomclaw.mandarin.core.RequestHelper;
import com.tomclaw.mandarin.im.AccountRoot;
import com.tomclaw.mandarin.im.CredentialsCheckCallback;
import com.tomclaw.mandarin.im.StatusUtil;
import com.tomclaw.mandarin.util.HttpUtil;
import com.tomclaw.mandarin.util.Logger;
/**
* Created with IntelliJ IDEA.
* User: anton
* Date: 3/31/13
* Time: 12:47 AM
*/
public class IcqAccountRoot extends AccountRoot {
// Network session.
private transient IcqSession icqSession;
// Client login variables.
private String tokenA;
private String sessionKey;
private long tokenExpirationDate;
private long timeDelta;
// Start session variables.
private String aimSid;
private String fetchBaseUrl;
private MyInfo myInfo;
private WellKnownUrls wellKnownUrls;
public IcqAccountRoot() {
icqSession = new IcqSession(this);
}
public IcqSession getSession() {
return icqSession;
}
@Override
public void connect() {
Logger.log("icq connection attempt");
// TODO: Such thread working model must be rewritten.
Thread connectThread = new Thread() {
private void sleep() {
try {
sleep(5000);
} catch (InterruptedException ignored) {
// No need to check.
}
}
public void run() {
do {
Logger.log("login: " + "start");
while (!checkSessionReady()) {
Logger.log("login: " + "session not ready");
while (!checkLoginReady()) {
Logger.log("login: " + "login not ready");
if (checkLoginExist()) {
Logger.log("login: " + "login exists, try to renew token");
// Try to renew token.
switch (icqSession.renewToken()) {
case IcqSession.EXTERNAL_UNKNOWN: {
Logger.log("login: " + "renew token external error");
if (isPasswordLogin()) {
Logger.log("login: " + "password login - reset login data");
// Reset login data and try to client login.
resetLoginData();
} else {
Logger.log("login: " + "can't renew token and no password :(");
// Show notification.
updateAccountState(StatusUtil.STATUS_OFFLINE, false);
onAuthLost();
return;
}
break;
}
case IcqSession.INTERNAL_ERROR: {
Logger.log("login: " + "renew token internal error");
sleep();
break;
}
}
} else {
Logger.log("login: " + "no login data, lets client login");
// Login with credentials.
switch (icqSession.clientLogin()) {
case IcqSession.EXTERNAL_LOGIN_ERROR: {
Logger.log("login: " + "client login error");
// Show notification.
updateAccountState(StatusUtil.STATUS_OFFLINE, false);
return;
}
case IcqSession.EXTERNAL_UNKNOWN: {
Logger.log("login: " + "client login external unknown");
// Show notification.
updateAccountState(StatusUtil.STATUS_OFFLINE, false);
return;
}
case IcqSession.INTERNAL_ERROR: {
Logger.log("login: " + "client login internal error");
// Sleep some time.
sleep();
break;
}
}
}
}
Logger.log("login: " + "start session attempt");
// Attempt to start session.
switch (icqSession.startSession()) {
case IcqSession.EXTERNAL_SESSION_OK: {
Logger.log("login: " + "session started ok");
break;
}
case IcqSession.EXTERNAL_SESSION_RATE_LIMIT: {
Logger.log("login: " + "start session rate limit");
// Show notification.
updateAccountState(StatusUtil.STATUS_OFFLINE, false);
return;
}
case IcqSession.EXTERNAL_UNKNOWN: {
Logger.log("login: " + "start session external error");
// Renew token or retry client login.
expireLoginData();
break;
}
case IcqSession.INTERNAL_ERROR: {
Logger.log("login: " + "start session internal error");
// Sleep some time.
sleep();
break;
}
}
}
Logger.log("login: " + "session ready, almost ok");
// Update account connecting state to false.
updateAccountState(false);
// Starting events fetching in verbal cycle.
} while (!icqSession.startEventsFetching());
// Update offline status.
updateAccountState(StatusUtil.STATUS_OFFLINE, false);
}
};
connectThread.start();
}
@Override
public void disconnect() {
RequestHelper.endSession(getContentResolver(), accountDbId);
}
public boolean isPasswordLogin() {
return !TextUtils.isEmpty(getUserPassword());
}
@Override
public void checkCredentials(final CredentialsCheckCallback callback) {
// TODO: Such thread working model must be rewritten.
Thread credentialsCheckThread = new Thread() {
private void sleep() {
try {
sleep(5000);
} catch (InterruptedException ignored) {
// No need to check.
}
}
public void run() {
while (!checkLoginReady()) {
switch (icqSession.clientLogin()) {
case IcqSession.EXTERNAL_LOGIN_ERROR:
case IcqSession.EXTERNAL_UNKNOWN: {
callback.onFailed();
return;
}
case IcqSession.INTERNAL_ERROR: {
// Sleep some time.
sleep();
break;
}
}
}
callback.onPassed();
}
};
credentialsCheckThread.start();
}
public void updateStatus() {
RequestHelper.requestSetState(getContentResolver(), getAccountDbId(),
getBaseStatusValue(statusIndex));
RequestHelper.requestSetMood(getContentResolver(), getAccountDbId(),
getMoodStatusValue(statusIndex), statusTitle, statusMessage);
}
protected int getBaseStatusValue(int statusIndex) {
int moodOffset = getStatusIndex(R.integer.mood_offset);
// Checking for status type - base or mood.
if (statusIndex >= moodOffset) {
statusIndex = getStatusIndex(R.integer.default_base_status);
}
return statusIndex;
}
protected int getMoodStatusValue(int statusIndex) {
int moodOffset = getStatusIndex(R.integer.mood_offset);
// Checking for status type - base or mood.
if (statusIndex < moodOffset) {
statusIndex = SetMoodRequest.STATUS_MOOD_RESET;
}
return statusIndex;
}
private int getStatusIndex(int resourceId) {
return getContext().getResources().getInteger(resourceId);
}
@Override
public String getAccountType() {
return getClass().getName();
}
public static int getStatusNamesResource() {
return R.array.status_names_icq;
}
public static int getStatusDrawablesResource() {
return R.array.status_drawable_icq;
}
public static int getStatusValuesResource() {
return R.array.status_values_icq;
}
public static int getStatusConnectResource() {
return R.array.status_connect_icq;
}
public static int getStatusSetupResource() {
return R.array.status_setup_icq;
}
public static int getStatusMusicResource() {
return R.integer.music_status;
}
public void setClientLoginResult(String login, String tokenA, String sessionKey,
long expiresIn, long hostTime) {
// Setup local variables.
this.userId = login;
this.tokenA = tokenA;
this.sessionKey = sessionKey;
this.timeDelta = hostTime - System.currentTimeMillis() / 1000;
this.tokenExpirationDate = expiresIn + hostTime;
// Save account data in database.
updateAccount();
}
public void setStartSessionResult(String aimSid, String fetchBaseUrl, WellKnownUrls wellKnownUrls) {
this.aimSid = aimSid;
this.fetchBaseUrl = fetchBaseUrl;
this.wellKnownUrls = wellKnownUrls;
}
public void setRenewTokenResult(String login, String tokenA, long expiresIn) {
// Setup local variables.
this.tokenA = tokenA;
this.tokenExpirationDate = expiresIn + getHostTime();
// Save account data in database.
updateAccount();
}
/**
* Updates account brief and status information. Also, updates account info in database.
*
* @param myInfo - protocol-based object with basic account info.
*/
protected void setMyInfo(MyInfo myInfo) {
this.myInfo = myInfo;
setUserNick(myInfo.getFriendly());
// Avatar checking and requesting.
String buddyIcon = myInfo.getBuddyIcon();
String bigBuddyIcon = myInfo.getBigBuddyIcon();
if (!TextUtils.isEmpty(bigBuddyIcon)) {
buddyIcon = bigBuddyIcon;
}
if (TextUtils.isEmpty(buddyIcon)) {
setAvatarHash(null);
} else {
if (!TextUtils.equals(getAvatarHash(), HttpUtil.getUrlHash(buddyIcon))) {
// Avatar is ready.
RequestHelper.requestAccountAvatar(getContentResolver(),
getAccountDbId(), buddyIcon);
}
}
// Update account status info.
String buddyStatus = myInfo.getState();
String moodIcon = myInfo.optMoodIcon();
String statusMessage = myInfo.optStatusMsg();
String moodTitle = myInfo.optMoodTitle();
int statusIndex = icqSession.getStatusIndex(moodIcon, buddyStatus);
String statusTitle = icqSession.getStatusTitle(moodTitle, statusIndex);
// Checking for we are disconnecting now.
if (getStatusIndex() != StatusUtil.STATUS_OFFLINE) {
// This will update account state and write account into db.
updateAccountState(statusIndex, statusTitle, statusMessage, false);
}
}
public boolean checkLoginExpired() {
return getHostTime() > tokenExpirationDate;
}
public boolean checkLoginExist() {
return !TextUtils.isEmpty(tokenA) && !TextUtils.isEmpty(sessionKey);
}
public boolean checkLoginReady() {
return !(TextUtils.isEmpty(tokenA) || TextUtils.isEmpty(sessionKey)
|| checkLoginExpired());
}
public boolean checkSessionReady() {
return !(TextUtils.isEmpty(aimSid) || TextUtils.isEmpty(fetchBaseUrl)
|| myInfo == null || wellKnownUrls == null);
}
public void expireLoginData() {
tokenExpirationDate = 0;
}
public void resetLoginData() {
tokenA = null;
sessionKey = null;
tokenExpirationDate = 0;
}
public void resetSessionData() {
aimSid = null;
fetchBaseUrl = null;
wellKnownUrls = null;
}
public long getHostTime() {
return timeDelta + System.currentTimeMillis() / 1000;
}
public void setHostTime(long hostTime) {
long actualTimeDelta = hostTime - System.currentTimeMillis() / 1000;
if (Math.abs(actualTimeDelta - timeDelta) > 5) {
Logger.log("Time delta updated from " + timeDelta + " to " + actualTimeDelta);
this.timeDelta = actualTimeDelta;
updateAccount();
}
}
public String getTokenA() {
return tokenA;
}
public String getSessionKey() {
return sessionKey;
}
public String getAimSid() {
return aimSid;
}
public String getFetchBaseUrl() {
return fetchBaseUrl;
}
public void setFetchBaseUrl(String fetchBaseUrl) {
this.fetchBaseUrl = fetchBaseUrl;
}
public MyInfo getMyInfo() {
return myInfo;
}
public WellKnownUrls getWellKnownUrls() {
return wellKnownUrls;
}
}