/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.actfm;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import org.json.JSONObject;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.Bundle;
import android.text.InputType;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.method.PasswordTransformationMethod;
import android.text.style.ClickableSpan;
import android.text.style.UnderlineSpan;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.facebook.Request;
import com.facebook.Request.GraphUserCallback;
import com.facebook.Response;
import com.facebook.Session;
import com.facebook.SessionState;
import com.facebook.UiLifecycleHelper;
import com.facebook.model.GraphUser;
import com.facebook.widget.LoginButton;
import com.google.android.googlelogin.GoogleLoginServiceConstants;
import com.google.android.googlelogin.GoogleLoginServiceHelper;
import com.timsu.astrid.GCMIntentService;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.actfm.sync.ActFmInvoker;
import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
import com.todoroo.astrid.actfm.sync.ActFmServiceException;
import com.todoroo.astrid.actfm.sync.ActFmSyncMonitor;
import com.todoroo.astrid.actfm.sync.ActFmSyncThread;
import com.todoroo.astrid.actfm.sync.messages.ConstructOutstandingTableFromMasterTable;
import com.todoroo.astrid.actfm.sync.messages.ConstructTaskOutstandingTableFromMasterTable;
import com.todoroo.astrid.actfm.sync.messages.NameMaps;
import com.todoroo.astrid.activity.Eula;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.RemoteModelDao;
import com.todoroo.astrid.dao.TagDataDao;
import com.todoroo.astrid.dao.TagMetadataDao;
import com.todoroo.astrid.dao.TagOutstandingDao;
import com.todoroo.astrid.dao.TaskAttachmentDao;
import com.todoroo.astrid.dao.TaskAttachmentOutstandingDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.TaskListMetadataDao;
import com.todoroo.astrid.dao.TaskListMetadataOutstandingDao;
import com.todoroo.astrid.dao.TaskOutstandingDao;
import com.todoroo.astrid.dao.UserActivityDao;
import com.todoroo.astrid.dao.UserActivityOutstandingDao;
import com.todoroo.astrid.dao.UserDao;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.TagOutstanding;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskListMetadata;
import com.todoroo.astrid.data.TaskListMetadataOutstanding;
import com.todoroo.astrid.data.UserActivity;
import com.todoroo.astrid.data.UserActivityOutstanding;
import com.todoroo.astrid.gtasks.auth.ModernAuthManager;
import com.todoroo.astrid.helper.UUIDHelper;
import com.todoroo.astrid.service.AstridDependencyInjector;
import com.todoroo.astrid.service.MarketStrategy.AmazonMarketStrategy;
import com.todoroo.astrid.service.StatisticsConstants;
import com.todoroo.astrid.service.StatisticsService;
import com.todoroo.astrid.service.SyncV2Service;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.subtasks.AstridOrderedListUpdater;
import com.todoroo.astrid.subtasks.AstridOrderedListUpdater.Node;
import com.todoroo.astrid.subtasks.SubtasksHelper;
import com.todoroo.astrid.subtasks.SubtasksHelper.TreeRemapHelper;
import com.todoroo.astrid.tags.TaskToTagMetadata;
/**
* This activity allows users to sign in or log in to Astrid.com
*
* @author Tim Su <tim@astrid.com>
*
*/
public class ActFmLoginActivity extends SherlockFragmentActivity {
@Autowired
protected Database database;
@Autowired
protected ExceptionService exceptionService;
@Autowired
protected TaskService taskService;
@Autowired
protected ActFmPreferenceService actFmPreferenceService;
@Autowired
private TaskDao taskDao;
@Autowired
private TaskOutstandingDao taskOutstandingDao;
@Autowired
private TaskAttachmentDao taskAttachmentDao;
@Autowired
private TaskAttachmentOutstandingDao taskAttachmentOutstandingDao;
@Autowired
private TagDataDao tagDataDao;
@Autowired
private TagOutstandingDao tagOutstandingDao;
@Autowired
private UserDao userDao;
@Autowired
private UserActivityDao userActivityDao;
@Autowired
private UserActivityOutstandingDao userActivityOutstandingDao;
@Autowired
private TaskListMetadataDao taskListMetadataDao;
@Autowired
private TaskListMetadataOutstandingDao taskListMetadataOutstandingDao;
@Autowired
private MetadataDao metadataDao;
@Autowired
private TagMetadataDao tagMetadataDao;
@Autowired protected SyncV2Service syncService;
private final ActFmInvoker actFmInvoker = new ActFmInvoker();
private Random rand;
protected TextView errors;
public static final String SHOW_TOAST = "show_toast"; //$NON-NLS-1$
// --- ui initialization
private static final int REQUEST_CODE_GOOGLE_ACCOUNTS = 1;
private static final int REQUEST_CODE_GOOGLE = 2;
static {
AstridDependencyInjector.initialize();
}
protected int getContentViewResource() {
return R.layout.actfm_login_activity;
}
protected int getTitleResource() {
return R.string.actfm_ALA_title;
}
public ActFmLoginActivity() {
super();
DependencyInjectionService.getInstance().inject(this);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ContextManager.setContext(this);
setContentView(getContentViewResource());
if(getTitleResource() != 0)
setTitle(getTitleResource());
if (getSupportActionBar() != null)
getSupportActionBar().hide();
rand = new Random(DateUtilities.now());
uiHelper = new UiLifecycleHelper(this, callback);
uiHelper.onCreate(savedInstanceState);
initializeUI();
getWindow().setFormat(PixelFormat.RGBA_8888);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DITHER);
recordPageView();
setResult(RESULT_CANCELED);
}
/* (non-Javadoc)
* @see android.app.Activity#onConfigurationChanged(android.content.res.Configuration)
*/
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(getContentViewResource());
}
@Override
protected void onResume() {
super.onResume();
uiHelper.onResume();
StatisticsService.sessionStart(this);
}
@Override
protected void onPause() {
super.onPause();
uiHelper.onPause();
StatisticsService.sessionPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
uiHelper.onDestroy();
}
@Override
protected void onStop() {
StatisticsService.sessionStop(this);
super.onStop();
}
protected void recordPageView() {
StatisticsService.reportEvent(StatisticsConstants.ACTFM_LOGIN_SHOW);
}
protected void setupTermsOfService(TextView tos) {
OnClickListener showTosListener = new OnClickListener() {
@Override
public void onClick(View v) {
Eula.showEulaBasic(ActFmLoginActivity.this);
}
};
tos.setOnClickListener(showTosListener);
String tosBase = getString(R.string.welcome_login_tos_base);
String tosLink = getString(R.string.welcome_login_tos_link);
SpannableString link = getLinkStringWithCustomInterval(tosBase, tosLink, tosBase.length() + 1, 0,
showTosListener);
tos.setText(link);
}
protected SpannableString getLinkStringWithCustomInterval(String base, String linkComponent,
int start, int endOffset, final OnClickListener listener) {
SpannableString link = new SpannableString (String.format("%s %s", //$NON-NLS-1$
base, linkComponent));
ClickableSpan linkSpan = new ClickableSpan() {
@Override
public void onClick(View widget) {
listener.onClick(widget);
}
@Override
public void updateDrawState(TextPaint ds) {
ds.setUnderlineText(true);
ds.setColor(Color.rgb(68, 68, 68));
}
};
link.setSpan(linkSpan, start, link.length() + endOffset, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return link;
}
@SuppressWarnings("nls")
protected void initializeUI() {
errors = (TextView) findViewById(R.id.error);
LoginButton loginButton = (LoginButton) findViewById(R.id.fb_login);
if(loginButton == null)
return;
loginButton.setReadPermissions(Arrays.asList("email", "offline_access"));
View googleLogin = findViewById(R.id.gg_login);
if(AmazonMarketStrategy.isKindleFire())
googleLogin.setVisibility(View.GONE);
googleLogin.setOnClickListener(googleListener);
View fbLogin = findViewById(R.id.fb_login_dummy);
fbLogin.setOnClickListener(facebookListener);
TextView signUp = (TextView) findViewById(R.id.pw_signup);
signUp.setOnClickListener(signUpListener);
TextView signIn = (TextView) findViewById(R.id.pw_login);
signIn.setOnClickListener(signInListener);
setupTermsOfService((TextView) findViewById(R.id.tos));
}
// --- event handler
protected final OnClickListener googleListener = new OnClickListener() {
@Override
public void onClick(View arg0) {
Intent intent = new Intent(ActFmLoginActivity.this,
ActFmGoogleAuthActivity.class);
startActivityForResult(intent, REQUEST_CODE_GOOGLE);
StatisticsService.reportEvent(StatisticsConstants.ACTFM_LOGIN_GL_START);
}
};
protected final OnClickListener facebookListener = new OnClickListener() {
@Override
public void onClick(View v) {
Session session = Session.getActiveSession();
if (session != null && session.isOpened()) {
facebookSuccess(session);
} else {
View fbLogin = findViewById(R.id.fb_login);
fbLogin.performClick();
}
}
};
protected final OnClickListener signUpListener = new OnClickListener() {
@Override
public void onClick(View v) {
final LinearLayout body = new LinearLayout(ActFmLoginActivity.this);
body.setOrientation(LinearLayout.VERTICAL);
body.setPadding(10, 0, 10, 0);
final EditText firstNameField = addEditField(body,
R.string.actfm_ALA_firstname_label);
firstNameField.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_VARIATION_PERSON_NAME |
InputType.TYPE_TEXT_FLAG_CAP_WORDS);
final EditText lastNameField = addEditField(body,
R.string.actfm_ALA_lastname_label);
lastNameField.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_VARIATION_PERSON_NAME |
InputType.TYPE_TEXT_FLAG_CAP_WORDS);
final EditText email = addEditField(body,
R.string.actfm_ALA_email_label);
email.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
getCredentials(new OnGetCredentials() {
@Override
public void getCredentials(String[] accounts) {
if (accounts != null && accounts.length > 0)
email.setText(accounts[0]);
}
});
ScrollView bodyScroll = new ScrollView(ActFmLoginActivity.this);
bodyScroll.addView(body);
new AlertDialog.Builder(ActFmLoginActivity.this).setView(
bodyScroll).setIcon(R.drawable.icon_32).setTitle(
R.string.actfm_ALA_signup_title).setPositiveButton(
android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dlg, int which) {
String firstName = firstNameField.getText().toString();
String lastName =lastNameField.getText().toString();
AndroidUtilities.hideSoftInputForViews(ActFmLoginActivity.this, firstNameField, lastNameField, email);
authenticate(email.getText().toString(),
firstName, lastName, ActFmInvoker.PROVIDER_PASSWORD, generateRandomPassword());
StatisticsService.reportEvent(StatisticsConstants.ACTFM_SIGNUP_PW);
}
}).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dlg, int which) {
AndroidUtilities.hideSoftInputForViews(ActFmLoginActivity.this, firstNameField, lastNameField, email);
}
}).show();
}
};
protected final OnClickListener signInListener = new OnClickListener() {
public void onClick(View v) {
final LinearLayout body = new LinearLayout(ActFmLoginActivity.this);
body.setOrientation(LinearLayout.VERTICAL);
body.setPadding(10, 0, 10, 0);
final EditText email = addEditField(body,
R.string.actfm_ALA_email_label);
email.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
getCredentials(new OnGetCredentials() {
@Override
public void getCredentials(String[] accounts) {
if (accounts != null && accounts.length > 0)
email.setText(accounts[0]);
}
});
final EditText password = addEditField(body,
R.string.actfm_ALA_password_label);
password.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_VARIATION_PASSWORD);
password.setTransformationMethod(new PasswordTransformationMethod());
TextView forgotPassword = new TextView(ActFmLoginActivity.this);
SpannableString text = new SpannableString(getString(R.string.actfm_ALA_forgot_password));
text.setSpan(new UnderlineSpan(), 0, text.length(), 0);
forgotPassword.setText(text);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
DisplayMetrics metrics = getResources().getDisplayMetrics();
lp.setMargins(0, (int) (8 * metrics.density), 0, (int) (8 * metrics.density));
forgotPassword.setLayoutParams(lp);
forgotPassword.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
forgotPassword(email.getText().toString());
}
});
body.addView(forgotPassword);
ScrollView bodyScroll = new ScrollView(ActFmLoginActivity.this);
bodyScroll.addView(body);
new AlertDialog.Builder(ActFmLoginActivity.this).setView(
bodyScroll).setIcon(R.drawable.icon_32).setTitle(
R.string.actfm_ALA_login_title).setPositiveButton(
android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dlg, int which) {
AndroidUtilities.hideSoftInputForViews(ActFmLoginActivity.this, email, password);
authenticate(email.getText().toString(),
"", "", ActFmInvoker.PROVIDER_PASSWORD, //$NON-NLS-1$//$NON-NLS-2$
password.getText().toString());
StatisticsService.reportEvent(StatisticsConstants.ACTFM_LOGIN_PW);
}
}).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dlg, int which) {
AndroidUtilities.hideSoftInputForViews(ActFmLoginActivity.this, email, password);
}
}).show();
}
};
private void forgotPassword(final String email) {
if (TextUtils.isEmpty(email)) {
DialogUtilities.okDialog(this, getString(R.string.actfm_ALA_enter_email), null);
} else {
final ProgressDialog pd = DialogUtilities.progressDialog(this, getString(R.string.DLG_please_wait));
new Thread(new Runnable() {
@Override
public void run() {
try {
actFmInvoker.invoke("user_reset_password", "email", email); //$NON-NLS-1$ //$NON-NLS-2$
DialogUtilities.okDialog(ActFmLoginActivity.this, getString(R.string.actfm_ALA_reset_sent, email), null);
} catch (IOException e) {
handleError(e);
} finally {
DialogUtilities.dismissDialog(ActFmLoginActivity.this, pd);
}
}
}).start();
}
}
private String generateRandomPassword() {
String acceptable = "abcdefghijklmnopqrstuvwxyz1234567890"; //$NON-NLS-1$
char[] chars = new char[8];
char last = 'a';
for (int i = 0; i < chars.length; i++) {
char r = acceptable.charAt(rand.nextInt(acceptable.length()));
while (!checkSimilar(last, r))
r = acceptable.charAt(rand.nextInt(acceptable.length()));
last = r;
chars[i] = r;
}
return new String(chars);
}
@SuppressWarnings("nls")
private boolean checkSimilar(char last, char check) {
String iSimilar = "ijl1!";
String oSimilar = "oO0";
String puncSimilar = ".,";
boolean match = (iSimilar.indexOf(last) > 0 && iSimilar.indexOf(check) > 0)
|| (oSimilar.indexOf(last) > 0 && oSimilar.indexOf(check) > 0)
|| (puncSimilar.indexOf(last) > 0 && puncSimilar.indexOf(check) > 0);
if (match)
return false;
return true;
}
private EditText addEditField(LinearLayout body, int hint) {
TextView label = new TextView(ActFmLoginActivity.this);
label.setText(hint);
body.addView(label);
EditText field = new EditText(ActFmLoginActivity.this);
field.setHint(hint);
body.addView(field);
return field;
}
// --- facebook handler
@SuppressWarnings("nls")
private void onSessionStateChange(Session session, SessionState state, Exception exception) {
if (state.isOpened()) {
Log.e("fb-login", "State opened");
facebookSuccess(session);
} else if (state.isClosed()) {
Log.e("fb-login", "State closed");
}
}
private void facebookSuccess(Session session) {
progressDialog = DialogUtilities.progressDialog(this,
getString(R.string.DLG_please_wait));
Request request = Request.newMeRequest(session, new GraphUserCallback() {
@Override
public void onCompleted(GraphUser user, Response response) {
try {
String email = user.getInnerJSONObject().optString("email"); //$NON-NLS-1$
authenticate(email, user.getFirstName(), user.getLastName(), ActFmInvoker.PROVIDER_FACEBOOK, Session.getActiveSession().getAccessToken());
} catch (Exception e) {
handleError(e);
}
}
});
request.executeAsync();
}
private UiLifecycleHelper uiHelper;
private final Session.StatusCallback callback = new Session.StatusCallback() {
@Override
public void call(Session session, SessionState state, Exception exception) {
onSessionStateChange(session, state, exception);
}
};
private ProgressDialog progressDialog;
// --- utilities
@SuppressWarnings("nls")
public void authenticate(final String email, final String firstName, final String lastName, final String provider,
final String secret) {
if (progressDialog == null)
progressDialog = DialogUtilities.progressDialog(this,
getString(R.string.DLG_please_wait));
new Thread() {
@Override
public void run() {
try {
final JSONObject result = actFmInvoker.authenticate(email, firstName, lastName,
provider, secret);
final String token = actFmInvoker.getToken();
if (result.optBoolean("new")) { // Report new user statistic
StatisticsService.reportEvent(StatisticsConstants.ACTFM_NEW_USER, "provider", provider);
}
// Successful login, create outstanding entries
String lastId = ActFmPreferenceService.userId(); //Preferences.getLong(ActFmPreferenceService.PREF_USER_ID, 0);
if (!TextUtils.isEmpty(token) && !RemoteModel.isValidUuid(lastId)) {
constructOutstandingTables();
}
runOnUiThread(new Runnable() {
public void run() {
DialogUtilities.dismissDialog(ActFmLoginActivity.this, progressDialog);
progressDialog = null;
postAuthenticate(result, token);
}
});
} catch (IOException e) {
handleError(e);
} finally {
runOnUiThread(new Runnable() {
public void run() {
if (progressDialog != null) {
DialogUtilities.dismissDialog(ActFmLoginActivity.this, progressDialog);
}
}
});
}
}
}.start();
}
private void constructOutstandingTables() {
new ConstructTaskOutstandingTableFromMasterTable(NameMaps.TABLE_ID_TASKS, taskDao, taskOutstandingDao, metadataDao, Task.CREATION_DATE).execute();
new ConstructOutstandingTableFromMasterTable<TagData, TagOutstanding>(NameMaps.TABLE_ID_TAGS, tagDataDao, tagOutstandingDao, TagData.CREATION_DATE).execute();
new ConstructOutstandingTableFromMasterTable<UserActivity, UserActivityOutstanding>(NameMaps.TABLE_ID_USER_ACTIVITY, userActivityDao, userActivityOutstandingDao, UserActivity.CREATED_AT).execute();
new ConstructOutstandingTableFromMasterTable<TaskListMetadata, TaskListMetadataOutstanding>(NameMaps.TABLE_ID_TASK_LIST_METADATA, taskListMetadataDao, taskListMetadataOutstandingDao, null).execute();
}
@SuppressWarnings("nls")
private void postAuthenticate(final JSONObject result, final String token) {
String lastLoggedInUser = ActFmPreferenceService.userId();
if (RemoteModel.isValidUuid(lastLoggedInUser)) {
String newUserId = Long.toString(result.optLong("id"));
if (!lastLoggedInUser.equals(newUserId)) {
// In this case, we need to either make all data private or clear all data
// Prompt for choice
DialogUtilities.okCancelCustomDialog(this,
getString(R.string.actfm_logged_in_different_user_title),
getString(R.string.actfm_logged_in_different_user_body),
R.string.actfm_logged_in_different_user_clear_data,
R.string.actfm_logged_in_different_user_keep_data,
android.R.drawable.ic_dialog_alert,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActFmSyncThread.clearTablePushedAtValues();
deleteDatabase(database.getName());
finishSignIn(result, token, true);
}
},
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
final ProgressDialog pd = DialogUtilities.progressDialog(ActFmLoginActivity.this, getString(R.string.actfm_logged_in_different_user_processing));
new Thread(new Runnable() {
@Override
public void run() {
rebuildAllSyncData();
runOnUiThread(new Runnable() {
@Override
public void run() {
finishSignIn(result, token, true);
}
});
pd.dismiss();
}
}).start();
}
});
} else {
finishSignIn(result, token, false);
}
} else {
finishSignIn(result, token, false);
}
}
private void rebuildAllSyncData() {
// Delete all tasks not assigned to self
taskService.deleteWhere(Criterion.or(Task.USER_ID.neq(0), Task.DELETION_DATE.gt(0)));
// Delete user table
userDao.deleteWhere(Criterion.all);
// Delete comments this user didn't make
userActivityDao.deleteWhere(Criterion.or(UserActivity.USER_UUID.neq(0), UserActivity.DELETED_AT.gt(0)));
// Delete attachments table
taskAttachmentDao.deleteWhere(Criterion.all);
// Delete deleted tags
tagDataDao.deleteWhere(TagData.DELETION_DATE.gt(0));
// Delete deleted metadata
metadataDao.deleteWhere(Metadata.DELETION_DATE.gt(0));
// Clear all outstanding tables
taskOutstandingDao.deleteWhere(Criterion.all);
tagOutstandingDao.deleteWhere(Criterion.all);
userActivityOutstandingDao.deleteWhere(Criterion.all);
taskListMetadataOutstandingDao.deleteWhere(Criterion.all);
taskAttachmentOutstandingDao.deleteWhere(Criterion.all);
// Make all tags private
tagMetadataDao.deleteWhere(Criterion.all);
// Generate new uuids for all tasks/tags/user activity/task list metadata and update links
generateNewUuids();
ActFmSyncThread.clearTablePushedAtValues();
constructOutstandingTables();
}
private void generateNewUuids() {
final HashMap<String, String> uuidTaskMap = new HashMap<String, String>();
HashMap<String, String> uuidTagMap = new HashMap<String, String>();
HashMap<String, String> uuidUserActivityMap = new HashMap<String, String>();
HashMap<String, String> uuidTaskListMetadataMap = new HashMap<String, String>();
mapUuids(taskDao, uuidTaskMap);
mapUuids(tagDataDao, uuidTagMap);
mapUuids(userActivityDao, uuidUserActivityMap);
mapUuids(taskListMetadataDao, uuidTaskListMetadataMap);
Task t = new Task();
TagData td = new TagData();
Metadata m = new Metadata();
UserActivity ua = new UserActivity();
TaskListMetadata tlm = new TaskListMetadata();
Set<Entry<String, String>> entries = uuidTaskMap.entrySet();
for (Entry<String, String> e : entries) {
t.clear();
m.clear();
ua.clear();
String oldUuid = e.getKey();
String newUuid = e.getValue();
t.setValue(Task.UUID, newUuid);
t.setValue(Task.PUSHED_AT, 0L);
t.setValue(Task.ATTACHMENTS_PUSHED_AT, 0L);
t.setValue(Task.USER_ACTIVITIES_PUSHED_AT, 0L);
t.setValue(Task.HISTORY_FETCH_DATE, 0L);
ua.setValue(UserActivity.TARGET_ID, newUuid);
m.setValue(TaskToTagMetadata.TASK_UUID, newUuid);
taskDao.update(Task.UUID.eq(oldUuid), t);
metadataDao.update(Criterion.and(MetadataCriteria.withKey(TaskToTagMetadata.KEY), TaskToTagMetadata.TASK_UUID.eq(oldUuid)), m);
userActivityDao.update(UserActivity.TARGET_ID.eq(oldUuid), ua);
}
entries = uuidTagMap.entrySet();
for (Entry<String, String> e : entries) {
td.clear();
ua.clear();
m.clear();
tlm.clear();
String oldUuid = e.getKey();
String newUuid = e.getValue();
td.setValue(TagData.UUID, newUuid);
td.setValue(TagData.USER_ID, Task.USER_ID_SELF);
td.setValue(TagData.PUSHED_AT, 0L);
td.setValue(TagData.TASKS_PUSHED_AT, 0L);
td.setValue(TagData.METADATA_PUSHED_AT, 0L);
td.setValue(TagData.USER_ACTIVITIES_PUSHED_AT, 0L);
ua.setValue(UserActivity.TARGET_ID, newUuid);
m.setValue(TaskToTagMetadata.TAG_UUID, newUuid);
tlm.setValue(TaskListMetadata.TAG_UUID, newUuid);
tagDataDao.update(TagData.UUID.eq(oldUuid), td);
userActivityDao.update(UserActivity.TARGET_ID.eq(oldUuid), ua);
metadataDao.update(Criterion.and(MetadataCriteria.withKey(TaskToTagMetadata.KEY), TaskToTagMetadata.TAG_UUID.eq(oldUuid)), m);
taskListMetadataDao.update(TaskListMetadata.TAG_UUID.eq(oldUuid), tlm);
}
entries = uuidUserActivityMap.entrySet();
for (Entry<String, String> e : entries) {
ua.clear();
String oldUuid = e.getKey();
String newUuid = e.getValue();
ua.setValue(UserActivity.UUID, newUuid);
ua.setValue(UserActivity.PUSHED_AT, 0L);
userActivityDao.update(UserActivity.UUID.eq(oldUuid), ua);
}
TodorooCursor<TaskListMetadata> tlmCursor = taskListMetadataDao.query(Query.select(TaskListMetadata.ID, TaskListMetadata.UUID, TaskListMetadata.FILTER, TaskListMetadata.TAG_UUID,
TaskListMetadata.TASK_IDS, TaskListMetadata.CHILD_TAG_IDS));
try {
for (tlmCursor.moveToFirst(); !tlmCursor.isAfterLast(); tlmCursor.moveToNext()) {
tlm.clear();
tlm.readFromCursor(tlmCursor);
String filterId = tlm.getValue(TaskListMetadata.FILTER);
String tagUuid = tlm.getValue(TaskListMetadata.TAG_UUID);
// Hack to make sure outstanding entry gets created for filter or uuid
if (!TextUtils.isEmpty(filterId)) {
tlm.setValue(TaskListMetadata.FILTER, ""); //$NON-NLS-1$
tlm.setValue(TaskListMetadata.FILTER, filterId);
} else if (!RemoteModel.isUuidEmpty(tagUuid)) {
tlm.setValue(TaskListMetadata.TAG_UUID, ""); //$NON-NLS-1$
tlm.setValue(TaskListMetadata.TAG_UUID, tagUuid);
}
tlm.setValue(TaskListMetadata.UUID, uuidTaskListMetadataMap.get(tlm.getUuid()));
tlm.setValue(TaskListMetadata.PUSHED_AT, 0L);
String taskIds = tlm.getValue(TaskListMetadata.TASK_IDS);
if (!TaskListMetadata.taskIdsIsEmpty(taskIds)) {
Node root = AstridOrderedListUpdater.buildTreeModel(taskIds, null);
SubtasksHelper.remapTree(root, uuidTaskMap, new TreeRemapHelper<String>() {
public String getKeyFromOldUuid(String uuid) {
return uuid; // Old uuids are the keys
}
});
taskIds = AstridOrderedListUpdater.serializeTree(root);
tlm.setValue(TaskListMetadata.TASK_IDS, taskIds);
}
taskListMetadataDao.saveExisting(tlm);
}
} finally {
tlmCursor.close();
}
}
private <T extends RemoteModel> void mapUuids(RemoteModelDao<T> dao, HashMap<String, String> map) {
TodorooCursor<T> items = dao.query(Query.select(RemoteModel.UUID_PROPERTY));
try {
for (items.moveToFirst(); !items.isAfterLast(); items.moveToNext()) {
map.put(items.get(RemoteModel.UUID_PROPERTY), UUIDHelper.newUUID());
}
} finally {
items.close();
}
}
@SuppressWarnings("nls")
private void finishSignIn(JSONObject result, String token, boolean restart) {
actFmPreferenceService.setToken(token);
Preferences.setString(ActFmPreferenceService.PREF_USER_ID,
Long.toString(result.optLong("id")));
Preferences.setString(ActFmPreferenceService.PREF_NAME,
result.optString("name"));
Preferences.setString(ActFmPreferenceService.PREF_FIRST_NAME,
result.optString("first_name"));
Preferences.setString(ActFmPreferenceService.PREF_LAST_NAME,
result.optString("last_name"));
Preferences.setBoolean(ActFmPreferenceService.PREF_PREMIUM,
result.optBoolean("premium"));
Preferences.setString(ActFmPreferenceService.PREF_EMAIL,
result.optString("email"));
Preferences.setString(ActFmPreferenceService.PREF_PICTURE,
result.optString("picture"));
if (!result.optBoolean("new"))
Toast.makeText(this, R.string.actfm_ALA_user_exists_sync_alert, Toast.LENGTH_LONG).show();
ActFmPreferenceService.reloadThisUser();
GCMIntentService.register(this);
if (restart) {
System.exit(0);
return;
} else {
setResult(RESULT_OK);
finish();
}
ActFmSyncMonitor monitor = ActFmSyncMonitor.getInstance();
synchronized (monitor) {
monitor.notifyAll();
}
}
@SuppressWarnings("nls")
private void handleError(final Throwable e) {
DialogUtilities.dismissDialog(this, progressDialog);
exceptionService.reportError("astrid-sharing-login", e); //$NON-NLS-1$
runOnUiThread(new Runnable() {
@Override
public void run() {
String message = e.getMessage();
if (e instanceof ActFmServiceException) {
ActFmServiceException ae = (ActFmServiceException) e;
JSONObject result = ae.result;
if (result != null && result.has("code")) {
String code = result.optString("code");
if ("user_exists".equals(code))
message = getString(R.string.actfm_ALA_error_user_exists);
else if ("incorrect_password".equals(code))
message = getString(R.string.actfm_ALA_error_wrong_password);
else if ("user_not_found".equals(code) || "missing_param".equals(code))
message = getString(R.string.actfm_ALA_error_user_not_found);
}
}
errors.setText(message);
errors.setVisibility(View.VISIBLE);
}
});
}
// --- google account manager
@SuppressWarnings("nls")
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
uiHelper.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_CANCELED)
return;
if (requestCode == REQUEST_CODE_GOOGLE_ACCOUNTS && data != null && credentialsListener != null) {
String accounts[] = data.getStringArrayExtra(
GoogleLoginServiceConstants.ACCOUNTS_KEY);
credentialsListener.getCredentials(accounts);
}
// else if (requestCode == LoginButton.REQUEST_CODE_FACEBOOK) {
// if (data == null)
// return;
//
// String error = data.getStringExtra("error");
// if (error == null) {
// error = data.getStringExtra("error_type");
// }
// String token = data.getStringExtra("access_token");
// if (error != null) {
// onFBAuthFail(error);
// } else if (token == null) {
// onFBAuthFail("Something went wrong! Please try again.");
// } else {
// facebook.setAccessToken(token);
// onFBAuthSucceed();
// }
// errors.setVisibility(View.GONE);
// }
else if (requestCode == REQUEST_CODE_GOOGLE) {
if (data == null)
return;
String email = data.getStringExtra(ActFmGoogleAuthActivity.RESULT_EMAIL);
String token = data.getStringExtra(ActFmGoogleAuthActivity.RESULT_TOKEN);
authenticate(email, email, "", "google", token);
}
}
public interface OnGetCredentials {
public void getCredentials(String[] accounts);
}
private OnGetCredentials credentialsListener;
public void getCredentials(OnGetCredentials onGetCredentials) {
credentialsListener = onGetCredentials;
if (Integer.parseInt(Build.VERSION.SDK) >= 7)
credentialsListener.getCredentials(ModernAuthManager.getAccounts(this));
else
GoogleLoginServiceHelper.getAccount(this,
REQUEST_CODE_GOOGLE_ACCOUNTS, false);
}
}