package info.guardianproject.otr.app.im.app;
import info.guardianproject.otr.app.im.IImConnection;
import info.guardianproject.otr.app.im.R;
import info.guardianproject.otr.app.im.app.adapter.ConnectionListenerAdapter;
import info.guardianproject.otr.app.im.engine.ImConnection;
import info.guardianproject.otr.app.im.engine.ImErrorInfo;
import info.guardianproject.otr.app.im.provider.Imps;
import info.guardianproject.otr.app.im.service.ImServiceConstants;
import info.guardianproject.util.LogCleaner;
import java.util.Collection;
import java.util.HashSet;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;
/**
* Handle sign-in process for activities.
*
* @author devrandom
*
* <p>Users of this helper must call {@link SignInHelper#stop()} to clean up callbacks
* in their onDestroy() or onPause() lifecycle methods.
*
* <p>The helper listens to connection events. It automatically stops listening when the
* connection state is logged-in or disconnected (failed).
*/
public class SignInHelper {
Activity mContext;
private SimpleAlertHandler mHandler;
private ImApp mApp;
private MyConnectionListener mListener;
private Collection<IImConnection> connections;
private SignInListener mSignInListener;
// This can be used to be informed of signin events
public interface SignInListener {
void connectedToService();
void stateChanged(int state, long accountId);
}
public SignInHelper(Activity context, SignInListener listener) {
this.mContext = context;
mHandler = new SimpleAlertHandler(context);
mListener = new MyConnectionListener(mHandler);
mSignInListener = listener;
if (mApp == null) {
mApp = (ImApp)mContext.getApplication();
}
connections = new HashSet<IImConnection>();
}
public SignInHelper(Activity context) {
this(context, null);
}
public void setSignInListener(SignInListener listener) {
mSignInListener = listener;
}
public void stop() {
for (IImConnection connection : connections) {
try {
connection.unregisterConnectionListener(mListener);
} catch (RemoteException e) {
// Ignore
}
}
connections.clear();
}
private final class MyConnectionListener extends ConnectionListenerAdapter {
MyConnectionListener(Handler handler) {
super(handler);
}
@Override
public void onConnectionStateChange(IImConnection connection, int state, ImErrorInfo error) {
handleConnectionEvent(connection, state, error);
}
}
private void handleConnectionEvent(IImConnection connection, int state, ImErrorInfo error) {
long accountId;
long providerId;
try {
accountId = connection.getAccountId();
providerId = connection.getProviderId();
} catch (RemoteException e) {
// Ouch! Service died! We'll just disappear.
Log.w(ImApp.LOG_TAG, "<SigningInActivity> Connection disappeared while signing in!");
return;
}
if (mSignInListener != null)
mSignInListener.stateChanged(state, accountId);
// Stop listening if we get into a resting state
if (state == ImConnection.LOGGED_IN || state == ImConnection.DISCONNECTED) {
connections.remove(connection);
try {
connection.unregisterConnectionListener(mListener);
} catch (RemoteException e) {
mHandler.showServiceErrorAlert(e.getLocalizedMessage());
LogCleaner.error(ImApp.LOG_TAG, "handle connection error",e);
}
}
if (state == ImConnection.DISCONNECTED) {
// sign in failed
final ProviderDef provider = mApp.getProvider(providerId);
String providerName = provider.mName;
Resources r = mContext.getResources();
String errMsg = r.getString(R.string.login_service_failed, providerName, // FIXME
error == null ? "" : ErrorResUtils.getErrorRes(r, error.getCode()));
Toast.makeText(mContext, errMsg, Toast.LENGTH_LONG).show();
/*
new AlertDialog.Builder(mContext).setTitle(R.string.error)
.setMessage()
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// FIXME
}
}).setCancelable(false).show();
*/
}
}
public void goToAccount(long accountId) {
Intent intent;
intent = new Intent(mContext, NewChatActivity.class);
// clear the back stack of the account setup
intent.putExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID, accountId);
mContext.startActivity(intent);
// sign in successfully, finish and switch to contact list
// mContext.finish();
}
public void signIn(final String password, final long providerId, final long accountId,
final boolean isActive) {
final ProviderDef provider = mApp.getProvider(providerId);
final String providerName = provider.mName;
if (mApp.serviceConnected()) {
if (mSignInListener != null)
mSignInListener.connectedToService();
if (!isActive) {
activateAccount(providerId, accountId);
}
signInAccount(password, providerId, providerName, accountId);
}
else
{
mApp.callWhenServiceConnected(mHandler, new Runnable() {
public void run() {
if (mApp.serviceConnected()) {
if (mSignInListener != null)
mSignInListener.connectedToService();
if (!isActive) {
activateAccount(providerId, accountId);
}
signInAccount(password, providerId, providerName, accountId);
}
}
});
}
}
private void signInAccount(String password, long providerId, String providerName, long accountId) {
boolean autoLoadContacts = true;
boolean autoRetryLogin = true;
IImConnection conn = null;
try {
conn = mApp.getConnection(providerId);
if (conn != null) {
connections.add(conn);
conn.registerConnectionListener(mListener);
int state = conn.getState();
if (mSignInListener != null)
mSignInListener.stateChanged(state, accountId);
if (state != ImConnection.DISCONNECTED) {
// already signed in or in the process
if (state == ImConnection.LOGGED_IN) {
connections.remove(conn);
conn.unregisterConnectionListener(mListener);
}
handleConnectionEvent(conn, state, null);
return;
}
} else {
conn = mApp.createConnection(providerId, accountId);
if (conn == null) {
// This can happen when service did not come up for any reason
return;
}
connections.add(conn);
conn.registerConnectionListener(mListener);
}
if (mApp.isNetworkAvailableAndConnected()) {
conn.login(password, autoLoadContacts, autoRetryLogin);
} else {
promptForBackgroundDataSetting(providerName);
return;
}
} catch (DeadObjectException e) {
try
{
conn = mApp.createConnection(providerId, accountId);
if (conn == null) {
// This can happen when service did not come up for any reason
return;
}
connections.add(conn);
conn.registerConnectionListener(mListener);
if (mApp.isNetworkAvailableAndConnected()) {
conn.login(password, autoLoadContacts, autoRetryLogin);
}
} catch (RemoteException e2) {
mHandler.showServiceErrorAlert(e2.getLocalizedMessage());
LogCleaner.error(ImApp.LOG_TAG, "sign in account",e2);
}
} catch (RemoteException e) {
mHandler.showServiceErrorAlert(e.getLocalizedMessage());
LogCleaner.error(ImApp.LOG_TAG, "sign in account",e);
}
}
private static final String SYNC_SETTINGS_ACTION = "android.settings.SYNC_SETTINGS";
private static final String SYNC_SETTINGS_CATEGORY = "android.intent.category.DEFAULT";
/**
* Popup a dialog to ask the user whether he/she wants to enable background
* connection to continue. If yes, enable the setting and broadcast the
* change. Otherwise, quit the signing in window immediately.
*/
private void promptForBackgroundDataSetting(String providerName) {
new AlertDialog.Builder(mContext)
.setTitle(R.string.bg_data_prompt_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(mContext.getString(R.string.bg_data_prompt_message, providerName))
.setPositiveButton(R.string.bg_data_prompt_ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
Intent intent = new Intent(SYNC_SETTINGS_ACTION);
intent.addCategory(SYNC_SETTINGS_CATEGORY);
mContext.startActivity(intent);
}
})
.setNegativeButton(R.string.bg_data_prompt_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
}
}).show();
}
public void activateAccount(long providerId, long accountId) {
// Update the active value. We restrict to only one active
// account per provider right now, so update all accounts of
// this provider to inactive first and then update this
// account to active.
ContentValues values = new ContentValues(1);
values.put(Imps.Account.ACTIVE, 0);
ContentResolver cr = mContext.getContentResolver();
cr.update(Imps.Account.CONTENT_URI, values, Imps.Account.PROVIDER + "=" + providerId, null);
values.put(Imps.Account.ACTIVE, 1);
cr.update(ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId), values, null,
null);
}
}