blob: 052b61745f9002f7f879a8818ddc2ab1cb9b9991 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.email.activity.setup;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.app.LoaderManager;
import android.app.ProgressDialog;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Loader;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.android.email.R;
import com.android.email.activity.UiUtilities;
import com.android.email.provider.AccountBackupRestore;
import com.android.email.service.EmailServiceUtils;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.EmailContent;
import com.android.mail.ui.MailAsyncTaskLoader;
import com.android.mail.utils.LogUtils;
public class AccountSetupFinal extends AccountSetupActivity implements View.OnClickListener {
private static final String SAVESTATE_IS_PROCESSING_KEY =
"com.android.email.AccountSetupFinal.is_processing";
private static final String SAVESTATE_STATE = "com.android.email.AccountSetupFinal.state";
private static final String ACCOUNT_CREATION_FRAGMENT_TAG = "AccountCreationFragment";
private static final String ACCOUNT_FINALIZE_FRAGMENT_TAG = "AccountFinalizeFragment";
private static final String CREATE_ACCOUNT_DIALOG_TAG = "CreateAccountDialog";
private static final String CONTENT_FRAGMENT_TAG = "AccountSetupContentFragment";
private static final int STATE_OPTIONS = 0;
private static final int STATE_NAMES = 1;
private int mState = STATE_OPTIONS;
private boolean mIsProcessing = false;
private Button mNextButton;
public static void actionFinal(Activity fromActivity, SetupDataFragment setupData) {
final Intent intent = new ForwardingIntent(fromActivity, AccountSetupFinal.class);
intent.putExtra(SetupDataFragment.EXTRA_SETUP_DATA, setupData);
fromActivity.startActivity(intent);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.account_setup);
if (savedInstanceState != null) {
mIsProcessing = savedInstanceState.getBoolean(SAVESTATE_IS_PROCESSING_KEY, false);
mState = savedInstanceState.getInt(SAVESTATE_STATE, STATE_OPTIONS);
} else {
// If we're not restoring from a previous state, we want to configure the initial screen
mState = STATE_OPTIONS;
updateHeadline();
updateContentFragment();
}
UiUtilities.getView(this, R.id.previous).setOnClickListener(this);
mNextButton = UiUtilities.getView(this, R.id.next);
mNextButton.setOnClickListener(this);
if (!mIsProcessing
&& mSetupData.getFlowMode() == SetupDataFragment.FLOW_MODE_FORCE_CREATE) {
// If we are just visiting here to fill in details, exit immediately
getFragmentManager().executePendingTransactions();
initiateAccountCreation();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(SAVESTATE_IS_PROCESSING_KEY, mIsProcessing);
outState.putInt(SAVESTATE_STATE, mState);
}
/**
* Set the headline text according to mState.
*/
private void updateHeadline() {
TextView headlineView = UiUtilities.getView(this, R.id.headline);
switch (mState) {
case STATE_OPTIONS:
headlineView.setText(R.string.account_setup_options_headline);
break;
case STATE_NAMES:
headlineView.setText(R.string.account_setup_names_headline);
break;
}
}
/**
* Swap in the new fragment according to mState. This pushes the current fragment onto the back
* stack, so only call it once per transition.
*/
private void updateContentFragment() {
final Fragment f;
switch (mState) {
case STATE_OPTIONS:
f = new AccountSetupOptionsFragment();
break;
case STATE_NAMES:
f = new AccountSetupNamesFragment();
break;
default:
throw new IllegalStateException("Unknown state " + mState);
}
final FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.setup_fragment_container, f, CONTENT_FRAGMENT_TAG);
ft.addToBackStack(null);
ft.commit();
}
/**
* Retrieve the current content fragment
* @return The content fragment or null if it wasn't found for some reason
*/
private Fragment getContentFragment() {
return getFragmentManager().findFragmentByTag(CONTENT_FRAGMENT_TAG);
}
/**
* Main choreography function to handle moving forward through scenes. Moving back should be
* generally handled for us by the back stack
*/
protected void proceed() {
mIsProcessing = false;
setNextButtonEnabled(true);
switch (mState) {
case STATE_OPTIONS:
mState = STATE_NAMES;
updateHeadline();
updateContentFragment();
if (mSetupData.getFlowMode() == SetupDataFragment.FLOW_MODE_FORCE_CREATE) {
getFragmentManager().executePendingTransactions();
initiateAccountFinalize();
}
break;
case STATE_NAMES:
finishActivity();
break;
}
}
/**
* Block the back key if we are currently processing the "next" key"
*/
@Override
public void onBackPressed() {
if (mIsProcessing) {
return;
}
if (mState == STATE_NAMES) {
finishActivity();
} else {
super.onBackPressed();
}
}
private void finishActivity() {
if (mSetupData.getFlowMode() == SetupDataFragment.FLOW_MODE_NO_ACCOUNTS) {
AccountSetupBasics.actionAccountCreateFinishedWithResult(this);
} else if (mSetupData.getFlowMode() != SetupDataFragment.FLOW_MODE_NORMAL) {
AccountSetupBasics.actionAccountCreateFinishedAccountFlow(this);
} else {
final Account account = mSetupData.getAccount();
if (account != null) {
AccountSetupBasics.actionAccountCreateFinished(this, account);
}
}
finish();
}
/**
* Respond to clicks in the "Next" or "Previous" buttons
*/
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.next:
// Debounce touches
if (!mIsProcessing) {
switch (mState) {
case STATE_OPTIONS:
initiateAccountCreation();
break;
case STATE_NAMES:
initiateAccountFinalize();
break;
}
setNextButtonEnabled(false);
}
break;
case R.id.previous:
onBackPressed();
break;
}
}
public void setNextButtonEnabled(final boolean enabled) {
mNextButton.setEnabled(enabled);
}
/**
* Ths is called when the user clicks the "done" button.
* It collects the data from the UI, updates the setup account record, and launches a fragment
* which handles creating the account in the system and database.
*/
private void initiateAccountCreation() {
mIsProcessing = true;
final Account account = mSetupData.getAccount();
if (account.mHostAuthRecv == null) {
throw new IllegalStateException("in AccountSetupOptions with null mHostAuthRecv");
}
final AccountSetupOptionsFragment fragment = (AccountSetupOptionsFragment)
getContentFragment();
if (fragment == null) {
throw new IllegalStateException("Fragment missing!");
}
account.setDisplayName(account.getEmailAddress());
int newFlags = account.getFlags() & ~(Account.FLAGS_BACKGROUND_ATTACHMENTS);
final EmailServiceUtils.EmailServiceInfo serviceInfo =
EmailServiceUtils.getServiceInfo(getApplicationContext(),
account.mHostAuthRecv.mProtocol);
if (serviceInfo.offerAttachmentPreload && fragment.getBackgroundAttachmentsValue()) {
newFlags |= Account.FLAGS_BACKGROUND_ATTACHMENTS;
}
account.setFlags(newFlags);
account.setSyncInterval(fragment.getCheckFrequencyValue());
final Integer syncWindowValue = fragment.getAccountSyncWindowValue();
if (syncWindowValue != null) {
account.setSyncLookback(syncWindowValue);
}
// Finish setting up the account, and commit it to the database
if (mSetupData.getPolicy() != null) {
account.mFlags |= Account.FLAGS_SECURITY_HOLD;
account.mPolicy = mSetupData.getPolicy();
}
// Finally, write the completed account (for the first time) and then
// install it into the Account manager as well. These are done off-thread.
// The account manager will report back via the callback, which will take us to
// the next operations.
final boolean syncEmail = fragment.getSyncEmailValue();
final boolean syncCalendar = serviceInfo.syncCalendar && fragment.getSyncCalendarValue();
final boolean syncContacts = serviceInfo.syncContacts && fragment.getSyncContactsValue();
final boolean enableNotifications = fragment.getNotifyValue();
final Fragment f = AccountCreationFragment.newInstance(account, syncEmail, syncCalendar,
syncContacts, enableNotifications);
final FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(f, ACCOUNT_CREATION_FRAGMENT_TAG);
ft.commit();
showCreateAccountDialog();
}
/**
* Called by the account creation fragment after it has completed.
* We do a small amount of work here before moving on to the next state.
*/
public void proceedFromAccountCreationFragment() {
destroyAccountCreationFragment();
// If the account manager initiated the creation, and success was not reported,
// then we assume that we're giving up (for any reason) - report failure.
final AccountAuthenticatorResponse authenticatorResponse =
mSetupData.getAccountAuthenticatorResponse();
if (authenticatorResponse != null) {
authenticatorResponse.onError(AccountManager.ERROR_CODE_CANCELED, "canceled");
mSetupData.setAccountAuthenticatorResponse(null);
}
proceed();
}
public void destroyAccountCreationFragment() {
dismissCreateAccountDialog();
final Fragment f = getFragmentManager().findFragmentByTag(ACCOUNT_CREATION_FRAGMENT_TAG);
if (f == null) {
LogUtils.wtf(LogUtils.TAG, "Couldn't find AccountCreationFragment to destroy");
}
final FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.remove(f);
ft.commit();
}
public static class CreateAccountDialogFragment extends DialogFragment {
CreateAccountDialogFragment() {}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
/// Show "Creating account..." dialog
final ProgressDialog d = new ProgressDialog(getActivity());
d.setIndeterminate(true);
d.setMessage(getString(R.string.account_setup_creating_account_msg));
return d;
}
}
protected void showCreateAccountDialog() {
new CreateAccountDialogFragment().show(getFragmentManager(), CREATE_ACCOUNT_DIALOG_TAG);
}
protected void dismissCreateAccountDialog() {
final DialogFragment f = (DialogFragment)
getFragmentManager().findFragmentByTag(CREATE_ACCOUNT_DIALOG_TAG);
if (f != null) {
f.dismiss();
}
}
public static class CreateAccountErrorDialogFragment extends DialogFragment
implements DialogInterface.OnClickListener {
public CreateAccountErrorDialogFragment() {}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final String message = getString(R.string.account_setup_failed_dlg_auth_message,
R.string.system_account_create_failed);
return new AlertDialog.Builder(getActivity())
.setIconAttribute(android.R.attr.alertDialogIcon)
.setTitle(getString(R.string.account_setup_failed_dlg_title))
.setMessage(message)
.setCancelable(true)
.setPositiveButton(
getString(R.string.account_setup_failed_dlg_edit_details_action), this)
.create();
}
@Override
public void onClick(DialogInterface dialog, int which) {
getActivity().finish();
}
}
/**
* This is called if MailService.setupAccountManagerAccount() fails for some reason
*/
protected void showCreateAccountErrorDialog() {
new CreateAccountErrorDialogFragment().show(getFragmentManager(), null);
}
private void initiateAccountFinalize() {
mIsProcessing = true;
AccountSetupNamesFragment fragment = (AccountSetupNamesFragment) getContentFragment();
// Update account object from UI
final Account account = mSetupData.getAccount();
final String description = fragment.getDescription();
if (!TextUtils.isEmpty(description)) {
account.setDisplayName(description);
}
account.setSenderName(fragment.getSenderName());
final Fragment f = AccountFinalizeFragment.newInstance(account);
final FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(f, ACCOUNT_FINALIZE_FRAGMENT_TAG);
ft.commit();
}
private static class AccountFinalizeFragment extends Fragment {
private static final String ACCOUNT_TAG = "account";
private static final int FINAL_ACCOUNT_TASK_LOADER_ID = 0;
private Context mAppContext;
public static AccountFinalizeFragment newInstance(Account account) {
final AccountFinalizeFragment f = new AccountFinalizeFragment();
final Bundle args = new Bundle(1);
args.putParcelable(ACCOUNT_TAG, account);
f.setArguments(args);
return f;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAppContext = getActivity().getApplicationContext();
setRetainInstance(true);
}
@Override
public void onResume() {
super.onResume();
getLoaderManager().initLoader(FINAL_ACCOUNT_TASK_LOADER_ID, getArguments(),
new LoaderManager.LoaderCallbacks<Boolean>() {
@Override
public Loader<Boolean> onCreateLoader(int id, Bundle args) {
final Account accountArg = args.getParcelable(ACCOUNT_TAG);
return new FinalSetupTaskLoader(mAppContext, accountArg);
}
@Override
public void onLoadFinished(Loader<Boolean> loader, Boolean success) {
if (success && isResumed()) {
AccountSetupFinal activity = (AccountSetupFinal) getActivity();
activity.finishActivity();
}
}
@Override
public void onLoaderReset(Loader<Boolean> loader) {
}
});
}
/**
* Final account setup work is handled in this Loader:
* Commit final values to provider
* Trigger account backup
*/
private static class FinalSetupTaskLoader extends MailAsyncTaskLoader<Boolean> {
private final Account mAccount;
public FinalSetupTaskLoader(Context context, Account account) {
super(context);
mAccount = account;
}
Account getAccount() {
return mAccount;
}
@Override
public Boolean loadInBackground() {
// Update the account in the database
final ContentValues cv = new ContentValues();
cv.put(EmailContent.AccountColumns.DISPLAY_NAME, mAccount.getDisplayName());
cv.put(EmailContent.AccountColumns.SENDER_NAME, mAccount.getSenderName());
mAccount.update(getContext(), cv);
// Update the backup (side copy) of the accounts
AccountBackupRestore.backup(getContext());
return true;
}
@Override
protected void onDiscardResult(Boolean result) {}
}
}
}