package kc.spark.pixels.android.ui; import static org.solemnsilence.util.Py.list; import static org.solemnsilence.util.Py.truthy; import kc.get.pixel.list.android.R; import kc.spark.pixels.android.app.DeviceState; import kc.spark.pixels.android.cloud.ApiFacade; import kc.spark.pixels.android.cloud.requestservice.SimpleSparkApiService; import kc.spark.pixels.android.ui.corelist.CoreListActivity; import kc.spark.pixels.android.ui.util.Ui; import org.apache.http.HttpStatus; import org.solemnsilence.util.EZ; import org.solemnsilence.util.TLog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.text.TextUtils; import android.text.method.LinkMovementMethod; import android.util.Patterns; import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnTouchListener; import android.view.inputmethod.EditorInfo; import android.widget.Button; import android.widget.EditText; import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; /** * Activity which displays a login screen to the user, offering registration as * well. */ public class SignUpActivity extends BaseActivity { private static final TLog log = new TLog(SignUpActivity.class); // Indicate that we came from the login screen, so don't attempt // "launchNextActivityIfReady()" public static final String EXTRA_FROM_LOGIN = "io.spark.core.android.extra.FROM_LOGIN"; // Values for email and password at the time of the login attempt. private String mEmail; private String mPassword; private String savedErrorMessage; private boolean waitForDevicesOnLogin = false; // UI references. private EditText mEmailView; private EditText mPasswordView; private Button accountAction; private LoggedInReceiver loggedInReceiver = new LoggedInReceiver(); private SignUpReceiver signUpReceiver = new SignUpReceiver(); private DevicesLoadedReceiver devicesLoadedReceiver = new DevicesLoadedReceiver(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent nextActivityToSkipTo = getNextActivityToSkipTo(); // NOTE EARLY RETURN HERE if (nextActivityToSkipTo != null) { if (!isFinishing()) { startActivity(nextActivityToSkipTo); } finish(); return; } setContentView(R.layout.activity_sign_up); mEmailView = Ui.findView(this, R.id.email); mPasswordView = Ui.findView(this, R.id.password); Ui.setTextFromHtml(this, R.id.already_have_account, R.string.i_already_have_an_account); TextView finePrint = Ui.setTextFromHtml(this, R.id.fine_print, R.string.sign_up_fine_print); finePrint.setMovementMethod(LinkMovementMethod.getInstance()); mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) { if (id == R.id.login || id == EditorInfo.IME_NULL) { attemptLogin(); return true; } return false; } }); accountAction = Ui.findView(this, R.id.sign_up_button); accountAction.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { attemptLogin(); } }); // set up touch listeners on form fields, to auto scroll when the // keyboard pops up for (View view : list(mPasswordView, mEmailView)) { view.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP) { scrollAccountForm(); } return false; } }); } findViewById(R.id.already_have_account).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(SignUpActivity.this, LoginActivity.class)); finish(); } }); } @Override protected void onStart() { super.onStart(); broadcastMgr.registerReceiver(signUpReceiver, signUpReceiver.getFilter()); broadcastMgr.registerReceiver(loggedInReceiver, loggedInReceiver.getFilter()); } @Override protected void onStop() { broadcastMgr.unregisterReceiver(signUpReceiver); broadcastMgr.unregisterReceiver(loggedInReceiver); broadcastMgr.unregisterReceiver(devicesLoadedReceiver); super.onStop(); } private void scrollAccountForm() { // delay this until after the keyboard has popped up so the scroll view // is actually able to scroll Runnable scrollRunnable = new Runnable() { @Override public void run() { ScrollView scrollArea = Ui.findView(SignUpActivity.this, R.id.scroll_area); // using fullScroll() or pageScroll() impacts which child widget // gets focus, so that doesn't work here, instead just scroll by // an absurdly large number. scrollArea.scrollBy(0, 5000); } }; // Doing this twice because sometimes it takes a moment for the keyboard // to pop up, changing the screen dimensions and allowing scrolling. EZ.runOnMainThreadDelayed(scrollRunnable, 150); EZ.runOnMainThreadDelayed(scrollRunnable, 750); } /** * Attempts to sign in or register the account specified by the login form. * If there are form errors (invalid email, missing fields, etc.), the * errors are presented and no actual login attempt is made. */ public void attemptLogin() { if (!accountAction.isEnabled()) { log.wtf("Sign up being attempted again even though the sign up button isn't enabled?!"); return; } // Reset errors. mEmailView.setError(null); mPasswordView.setError(null); // Store values at the time of the login attempt. mEmail = mEmailView.getText().toString(); mPassword = mPasswordView.getText().toString(); boolean cancel = false; View focusView = null; // Check for a valid email address. if (!truthy(mEmail)) { mEmailView.setError(getString(R.string.error_field_required)); focusView = mEmailView; cancel = true; } else if (!Patterns.EMAIL_ADDRESS.matcher(mEmail).matches()) { mEmailView.setError(getString(R.string.error_invalid_email)); focusView = mEmailView; cancel = true; } // Check for a valid password. if (TextUtils.isEmpty(mPassword)) { mPasswordView.setError(getString(R.string.error_field_required)); focusView = mPasswordView; cancel = true; } else if (mPassword.length() < 4) { mPasswordView.setError(getString(R.string.error_invalid_password)); focusView = mPasswordView; cancel = true; } if (cancel) { // There was an error; don't attempt login and focus the first // form field with an error. focusView.requestFocus(); } else { // Show a progress spinner, and kick off a background task to // perform the user login attempt. showProgress(true); ApiFacade.getInstance(this).signUp(mEmail, mPassword); } } // If we're ready to skip to a new activity, return it here. private Intent getNextActivityToSkipTo() { if (getIntent().hasExtra(EXTRA_FROM_LOGIN)) { // we came here from the login screen, don't bounce back and forth. return null; } Intent intent = null; if (isLoggedIn()) { log.d("Looks like we're logged in, launching Cores list"); intent = new Intent(this, CoreListActivity.class) .putExtra(CoreListActivity.ARG_ENTERING_FROM_LAUNCH, true); if (DeviceState.getKnownDevices().isEmpty()) { api.requestAllDevices(); } } else if (prefs.getCompletedFirstLogin()) { log.d("We're not logged in now, but we have been before, launching login screen"); intent = new Intent(this, LoginActivity.class); } if (intent == null) { log.d("User is not logged in and has never logged in, staying on sign up screen"); } else { return intent; } return null; } private boolean isLoggedIn() { // i.e.: do we have a token? return truthy(prefs.getToken()); } /** * Shows the progress UI and hides the login form. */ private void showProgress(final boolean show) { super.showProgress(R.id.progress_indicator, show); accountAction.setEnabled(!show); } private void onSignUpComplete(boolean success, String error) { // Regardless of whether it's a success, wait for the service to try // logging in before reporting failure. if (!success) { savedErrorMessage = error; // sign up might have "failed" because the user has an account // already waitForDevicesOnLogin = true; } } private void onLogInComplete(boolean success, String error) { if (!success) { waitForDevicesOnLogin = false; // clear this state onFailed(savedErrorMessage); return; } if (waitForDevicesOnLogin) { broadcastMgr.registerReceiver(devicesLoadedReceiver, devicesLoadedReceiver.getFilter()); api.requestAllDevices(); Toast toast = Toast.makeText(this, "Loading your Cores...", Toast.LENGTH_LONG); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); } else { moveToCoreList(); } } private void moveToCoreList() { if (!isFinishing()) { Intent intent = new Intent(this, CoreListActivity.class) .putExtra(CoreListActivity.ARG_ENTERING_FROM_LAUNCH, true); if (DeviceState.getKnownDevices().isEmpty()) { intent.putExtra(CoreListActivity.ARG_SKIP_TO_SMART_CONFIG, true); } startActivity(intent); } finish(); } private void onFailed(String error) { showProgress(false); mPasswordView.requestFocus(); mPasswordView.setError(error); savedErrorMessage = null; } private void onDevicesUpdated(boolean success, String error) { if (success) { moveToCoreList(); } } class SignUpReceiver extends BroadcastReceiver { public IntentFilter getFilter() { return new IntentFilter(ApiFacade.BROADCAST_SIGN_UP_FINISHED); } @Override public void onReceive(Context context, Intent intent) { onSignUpComplete((ApiFacade.getResultCode(intent) == HttpStatus.SC_OK), intent.getStringExtra(SimpleSparkApiService.EXTRA_ERROR_MSG)); } } class LoggedInReceiver extends BroadcastReceiver { public IntentFilter getFilter() { return new IntentFilter(ApiFacade.BROADCAST_LOG_IN_FINISHED); } @Override public void onReceive(Context context, Intent intent) { onLogInComplete((ApiFacade.getResultCode(intent) == HttpStatus.SC_OK), intent.getStringExtra(SimpleSparkApiService.EXTRA_ERROR_MSG)); } } class DevicesLoadedReceiver extends BroadcastReceiver { public IntentFilter getFilter() { return new IntentFilter(ApiFacade.BROADCAST_DEVICES_UPDATED); } @Override public void onReceive(Context context, Intent intent) { onDevicesUpdated((ApiFacade.getResultCode(intent) == HttpStatus.SC_OK), intent.getStringExtra(SimpleSparkApiService.EXTRA_ERROR_MSG)); } } }