michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.sync.setup.activities; michael@0: michael@0: import java.util.Locale; michael@0: michael@0: import org.mozilla.gecko.R; michael@0: import org.mozilla.gecko.background.common.log.Logger; michael@0: import org.mozilla.gecko.sync.SyncConstants; michael@0: import org.mozilla.gecko.sync.ThreadPool; michael@0: import org.mozilla.gecko.sync.setup.Constants; michael@0: import org.mozilla.gecko.sync.setup.InvalidSyncKeyException; michael@0: import org.mozilla.gecko.sync.setup.SyncAccounts; michael@0: import org.mozilla.gecko.sync.setup.SyncAccounts.SyncAccountParameters; michael@0: import org.mozilla.gecko.sync.setup.auth.AccountAuthenticator; michael@0: import org.mozilla.gecko.sync.setup.auth.AuthenticationResult; michael@0: michael@0: import android.accounts.Account; michael@0: import android.accounts.AccountAuthenticatorActivity; michael@0: import android.accounts.AccountManager; michael@0: import android.app.ProgressDialog; michael@0: import android.content.Context; michael@0: import android.content.Intent; michael@0: import android.os.Bundle; michael@0: import android.text.Editable; michael@0: import android.text.TextWatcher; michael@0: import android.view.View; michael@0: import android.view.View.OnClickListener; michael@0: import android.view.Window; michael@0: import android.view.WindowManager; michael@0: import android.widget.Button; michael@0: import android.widget.CheckBox; michael@0: import android.widget.CompoundButton; michael@0: import android.widget.CompoundButton.OnCheckedChangeListener; michael@0: import android.widget.EditText; michael@0: import android.widget.Toast; michael@0: michael@0: public class AccountActivity extends AccountAuthenticatorActivity { michael@0: private final static String LOG_TAG = "AccountActivity"; michael@0: michael@0: private AccountManager mAccountManager; michael@0: private Context mContext; michael@0: private String username; michael@0: private String password; michael@0: private String key; michael@0: private String server = SyncConstants.DEFAULT_AUTH_SERVER; michael@0: michael@0: // UI elements. michael@0: private EditText serverInput; michael@0: private EditText usernameInput; michael@0: private EditText passwordInput; michael@0: private EditText synckeyInput; michael@0: private CheckBox serverCheckbox; michael@0: private Button connectButton; michael@0: private Button cancelButton; michael@0: private ProgressDialog progressDialog; michael@0: michael@0: private AccountAuthenticator accountAuthenticator; michael@0: michael@0: @Override michael@0: public void onCreate(Bundle savedInstanceState) { michael@0: super.onCreate(savedInstanceState); michael@0: setContentView(R.layout.sync_account); michael@0: michael@0: ActivityUtils.prepareLogging(); michael@0: mContext = getApplicationContext(); michael@0: Logger.debug(LOG_TAG, "AccountManager.get(" + mContext + ")"); michael@0: mAccountManager = AccountManager.get(mContext); michael@0: michael@0: // Set "screen on" flag. michael@0: Logger.debug(LOG_TAG, "Setting screen-on flag."); michael@0: Window w = getWindow(); michael@0: w.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); michael@0: michael@0: // Find UI elements. michael@0: usernameInput = (EditText) findViewById(R.id.usernameInput); michael@0: passwordInput = (EditText) findViewById(R.id.passwordInput); michael@0: synckeyInput = (EditText) findViewById(R.id.keyInput); michael@0: serverInput = (EditText) findViewById(R.id.serverInput); michael@0: michael@0: TextWatcher inputValidator = makeInputValidator(); michael@0: michael@0: usernameInput.addTextChangedListener(inputValidator); michael@0: passwordInput.addTextChangedListener(inputValidator); michael@0: synckeyInput.addTextChangedListener(inputValidator); michael@0: serverInput.addTextChangedListener(inputValidator); michael@0: michael@0: connectButton = (Button) findViewById(R.id.accountConnectButton); michael@0: cancelButton = (Button) findViewById(R.id.accountCancelButton); michael@0: serverCheckbox = (CheckBox) findViewById(R.id.checkbox_server); michael@0: michael@0: serverCheckbox.setOnCheckedChangeListener(new OnCheckedChangeListener() { michael@0: @Override michael@0: public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { michael@0: Logger.info(LOG_TAG, "Toggling checkbox: " + isChecked); michael@0: if (!isChecked) { // Clear server input. michael@0: serverInput.setVisibility(View.GONE); michael@0: findViewById(R.id.server_error).setVisibility(View.GONE); michael@0: serverInput.setText(""); michael@0: } else { michael@0: serverInput.setVisibility(View.VISIBLE); michael@0: serverInput.setEnabled(true); michael@0: } michael@0: // Activate connectButton if necessary. michael@0: activateView(connectButton, validateInputs()); michael@0: } michael@0: }); michael@0: } michael@0: michael@0: @Override michael@0: public void onResume() { michael@0: super.onResume(); michael@0: ActivityUtils.prepareLogging(); michael@0: clearCredentials(); michael@0: usernameInput.requestFocus(); michael@0: cancelButton.setOnClickListener(new OnClickListener() { michael@0: michael@0: @Override michael@0: public void onClick(View v) { michael@0: cancelClickHandler(v); michael@0: } michael@0: michael@0: }); michael@0: } michael@0: michael@0: public void cancelClickHandler(View target) { michael@0: finish(); michael@0: } michael@0: michael@0: public void cancelConnectHandler(View target) { michael@0: if (accountAuthenticator != null) { michael@0: accountAuthenticator.isCanceled = true; michael@0: accountAuthenticator = null; michael@0: } michael@0: displayVerifying(false); michael@0: activateView(connectButton, true); michael@0: clearCredentials(); michael@0: usernameInput.requestFocus(); michael@0: } michael@0: michael@0: private void clearCredentials() { michael@0: // Only clear password. Re-typing the sync key or email is annoying. michael@0: passwordInput.setText(""); michael@0: } michael@0: /* michael@0: * Get credentials on "Connect" and write to AccountManager, where it can be michael@0: * accessed by Fennec and Sync Service. michael@0: */ michael@0: public void connectClickHandler(View target) { michael@0: Logger.debug(LOG_TAG, "connectClickHandler for view " + target); michael@0: // Validate sync key format. michael@0: try { michael@0: key = ActivityUtils.validateSyncKey(synckeyInput.getText().toString()); michael@0: } catch (InvalidSyncKeyException e) { michael@0: // Toast: invalid sync key format. michael@0: Toast toast = Toast.makeText(mContext, R.string.sync_new_recoverykey_status_incorrect, Toast.LENGTH_LONG); michael@0: toast.show(); michael@0: return; michael@0: } michael@0: username = usernameInput.getText().toString().toLowerCase(Locale.US); michael@0: password = passwordInput.getText().toString(); michael@0: key = synckeyInput.getText().toString(); michael@0: server = SyncConstants.DEFAULT_AUTH_SERVER; michael@0: michael@0: if (serverCheckbox.isChecked()) { michael@0: String userServer = serverInput.getText().toString(); michael@0: if (userServer != null) { michael@0: userServer = userServer.trim(); michael@0: if (userServer.length() != 0) { michael@0: if (!userServer.startsWith("https://") && michael@0: !userServer.startsWith("http://")) { michael@0: // Assume HTTPS if not specified. michael@0: userServer = "https://" + userServer; michael@0: serverInput.setText(userServer); michael@0: } michael@0: server = userServer; michael@0: } michael@0: } michael@0: } michael@0: michael@0: clearErrors(); michael@0: displayVerifying(true); michael@0: cancelButton.setOnClickListener(new OnClickListener() { michael@0: @Override michael@0: public void onClick(View v) { michael@0: cancelConnectHandler(v); michael@0: // Set cancel click handler to leave account setup. michael@0: cancelButton.setOnClickListener(new OnClickListener() { michael@0: public void onClick(View v) { michael@0: cancelClickHandler(v); michael@0: } michael@0: }); michael@0: } michael@0: }); michael@0: michael@0: accountAuthenticator = new AccountAuthenticator(this); michael@0: accountAuthenticator.authenticate(server, username, password); michael@0: } michael@0: michael@0: private TextWatcher makeInputValidator() { michael@0: return new TextWatcher() { michael@0: michael@0: @Override michael@0: public void afterTextChanged(Editable s) { michael@0: activateView(connectButton, validateInputs()); michael@0: } michael@0: michael@0: @Override michael@0: public void beforeTextChanged(CharSequence s, int start, int count, michael@0: int after) { michael@0: } michael@0: michael@0: @Override michael@0: public void onTextChanged(CharSequence s, int start, int before, int count) { michael@0: } michael@0: }; michael@0: } michael@0: michael@0: private boolean validateInputs() { michael@0: if (usernameInput.length() == 0 || michael@0: passwordInput.length() == 0 || michael@0: synckeyInput.length() == 0 || michael@0: (serverCheckbox.isChecked() && michael@0: serverInput.length() == 0)) { michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * Callback that handles auth based on success/failure michael@0: */ michael@0: public void authCallback(final AuthenticationResult result) { michael@0: displayVerifying(false); michael@0: if (result != AuthenticationResult.SUCCESS) { michael@0: Logger.debug(LOG_TAG, "displayFailure()"); michael@0: displayFailure(result); michael@0: return; michael@0: } michael@0: // Successful authentication. Create and add account to AccountManager. michael@0: SyncAccountParameters syncAccount = new SyncAccountParameters( michael@0: mContext, mAccountManager, username, key, password, server); michael@0: createAccountOnThread(syncAccount); michael@0: } michael@0: michael@0: private void createAccountOnThread(final SyncAccountParameters syncAccount) { michael@0: ThreadPool.run(new Runnable() { michael@0: @Override michael@0: public void run() { michael@0: Account account = SyncAccounts.createSyncAccount(syncAccount); michael@0: boolean isSuccess = (account != null); michael@0: if (!isSuccess) { michael@0: setResult(RESULT_CANCELED); michael@0: runOnUiThread(new Runnable() { michael@0: @Override michael@0: public void run() { michael@0: displayFailure(AuthenticationResult.FAILURE_ACCOUNT); michael@0: } michael@0: }); michael@0: return; michael@0: } michael@0: michael@0: // Account created successfully. michael@0: clearErrors(); michael@0: michael@0: Bundle resultBundle = new Bundle(); michael@0: resultBundle.putString(AccountManager.KEY_ACCOUNT_NAME, syncAccount.username); michael@0: resultBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, SyncConstants.ACCOUNTTYPE_SYNC); michael@0: resultBundle.putString(AccountManager.KEY_AUTHTOKEN, SyncConstants.ACCOUNTTYPE_SYNC); michael@0: setAccountAuthenticatorResult(resultBundle); michael@0: michael@0: setResult(RESULT_OK); michael@0: runOnUiThread(new Runnable() { michael@0: @Override michael@0: public void run() { michael@0: authSuccess(); michael@0: } michael@0: }); michael@0: } michael@0: }); michael@0: } michael@0: michael@0: private void displayVerifying(final boolean isVerifying) { michael@0: if (isVerifying) { michael@0: progressDialog = ProgressDialog.show(AccountActivity.this, "", getString(R.string.sync_verifying_label), true); michael@0: } else { michael@0: progressDialog.dismiss(); michael@0: } michael@0: } michael@0: michael@0: private void displayFailure(final AuthenticationResult result) { michael@0: runOnUiThread(new Runnable() { michael@0: @Override michael@0: public void run() { michael@0: Intent intent; michael@0: switch (result) { michael@0: case FAILURE_USERNAME: michael@0: // No such username. Don't leak whether the username exists. michael@0: case FAILURE_PASSWORD: michael@0: findViewById(R.id.cred_error).setVisibility(View.VISIBLE); michael@0: usernameInput.requestFocus(); michael@0: break; michael@0: case FAILURE_SERVER: michael@0: findViewById(R.id.server_error).setVisibility(View.VISIBLE); michael@0: serverInput.requestFocus(); michael@0: break; michael@0: case FAILURE_ACCOUNT: michael@0: intent = new Intent(mContext, SetupFailureActivity.class); michael@0: intent.setFlags(Constants.FLAG_ACTIVITY_REORDER_TO_FRONT_NO_ANIMATION); michael@0: intent.putExtra(Constants.INTENT_EXTRA_IS_ACCOUNTERROR, true); michael@0: startActivity(intent); michael@0: break; michael@0: case FAILURE_OTHER: michael@0: default: michael@0: // Display default error screen. michael@0: Logger.debug(LOG_TAG, "displaying default failure."); michael@0: intent = new Intent(mContext, SetupFailureActivity.class); michael@0: intent.setFlags(Constants.FLAG_ACTIVITY_REORDER_TO_FRONT_NO_ANIMATION); michael@0: startActivity(intent); michael@0: } michael@0: } michael@0: }); michael@0: } michael@0: michael@0: /** michael@0: * Feedback to user of account setup success. michael@0: */ michael@0: public void authSuccess() { michael@0: // Display feedback of successful account setup. michael@0: Intent intent = new Intent(mContext, SetupSuccessActivity.class); michael@0: intent.setFlags(Constants.FLAG_ACTIVITY_REORDER_TO_FRONT_NO_ANIMATION); michael@0: startActivity(intent); michael@0: finish(); michael@0: } michael@0: michael@0: private void activateView(View view, boolean toActivate) { michael@0: view.setEnabled(toActivate); michael@0: view.setClickable(toActivate); michael@0: } michael@0: michael@0: private void clearErrors() { michael@0: runOnUiThread(new Runnable() { michael@0: @Override michael@0: public void run() { michael@0: findViewById(R.id.cred_error).setVisibility(View.GONE); michael@0: findViewById(R.id.server_error).setVisibility(View.GONE); michael@0: } michael@0: }); michael@0: } michael@0: }