content: package im.actor.sdk.controllers.auth;
  
  import android.app.AlertDialog;
  import android.app.ProgressDialog;
  import android.content.DialogInterface;
  import android.content.Intent;
  import android.os.Bundle;
  import android.support.v4.app.Fragment;
  import android.view.MenuItem;
  
  import im.actor.core.AuthState;
  import im.actor.core.entity.AuthCodeRes;
  import im.actor.core.entity.AuthRes;
  import im.actor.core.entity.AuthStartRes;
  import im.actor.core.entity.Sex;
  import im.actor.core.network.RpcException;
  import im.actor.core.network.RpcInternalException;
  import im.actor.core.network.RpcTimeoutException;
  import im.actor.runtime.actors.Actor;
  import im.actor.runtime.actors.ActorCreator;
  import im.actor.runtime.actors.ActorRef;
  import im.actor.runtime.actors.ActorSystem;
  import im.actor.runtime.actors.Props;
  import im.actor.runtime.function.Consumer;
  import im.actor.runtime.promise.Promise;
  import im.actor.runtime.storage.PreferencesStorage;
  import im.actor.sdk.ActorSDK;
  import im.actor.sdk.R;
  import im.actor.sdk.controllers.root.RootActivity;
  import im.actor.sdk.controllers.activity.BaseFragmentActivity;
  
  import static im.actor.sdk.util.ActorSDKMessenger.messenger;
  
  public class AuthActivity extends BaseFragmentActivity {
  
      public static final String AUTH_TYPE_KEY = "auth_type";
      public static final String SIGN_TYPE_KEY = "sign_type";
      public static final int AUTH_TYPE_PHONE = 1;
      public static final int AUTH_TYPE_EMAIL = 2;
  
      public static final int SIGN_TYPE_IN = 3;
      public static final int SIGN_TYPE_UP = 4;
      private static final int OAUTH_DIALOG = 1;
      private ProgressDialog progressDialog;
      private AlertDialog alertDialog;
      private AuthState state;
      private int availableAuthType = AUTH_TYPE_PHONE;
      private int currentAuthType = AUTH_TYPE_PHONE;
      private int signType;
      private long currentPhone;
      private String currentEmail;
      private String transactionHash;
      private String currentCode;
      private boolean isRegistered = false;
      private String currentName;
      private Sex currentSex;
      private ActorRef authActor;
      private boolean codeValidated = false;
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          authActor = ActorSystem.system().actorOf(Props.create(new ActorCreator() {
              @Override
              public Actor create() {
                  return new Actor();
              }
          }), "actor/auth_promises_actor");
  
          signType = getIntent().getIntExtra(SIGN_TYPE_KEY, SIGN_TYPE_IN);
  
          PreferencesStorage preferences = messenger().getPreferences();
          currentPhone = preferences.getLong("currentPhone", 0);
          currentEmail = preferences.getString("currentEmail");
          transactionHash = preferences.getString("transactionHash");
          isRegistered = preferences.getBool("isRegistered", false);
          codeValidated = preferences.getBool("codeValidated", false);
          currentName = preferences.getString("currentName");
          signType = preferences.getInt("signType", signType);
          String savedState = preferences.getString("auth_state");
          state = Enum.valueOf(AuthState.class, savedState != null ? savedState : "AUTH_START");
          updateState(state, true);
      }
  
  
      @Override
      public boolean onOptionsItemSelected(MenuItem item) {
          if (item.getItemId() == android.R.id.home) {
          }
          return super.onOptionsItemSelected(item);
      }
  
  
      private void updateState(AuthState state) {
          updateState(state, false);
      }
  
      private void updateState(AuthState state, boolean force) {
          if (this.state != null && (this.state == state && !force)) {
              return;
          }
          PreferencesStorage preferences = messenger().getPreferences();
          preferences.putLong("currentPhone", currentPhone);
          preferences.putString("currentEmail", currentEmail);
          preferences.putString("transactionHash", transactionHash);
          preferences.putBool("isRegistered", isRegistered);
          preferences.putBool("codeValidated", codeValidated);
          preferences.putString("currentName", currentName);
          preferences.putInt("signType", signType);
          preferences.putString("auth_state", state.toString());
  
          // if we show the next fragment when app is in background and not visible , app crashes!
          // e.g when the GSM data is off and after trying to send code we go to settings to turn on, app is going invisible and ...
          if (state != AuthState.LOGGED_IN && getIsResumed() == false) {
              return;
          }
  
          this.state = state;
  
          switch (state) {
              case AUTH_START:
  
                  if (signType == SIGN_TYPE_UP) {
                      updateState(AuthState.SIGN_UP);
                  } else if (signType == SIGN_TYPE_IN) {
                      showFragment(new SignInFragment(), false);
                  }
  
                  break;
              case SIGN_UP:
                  if (currentName != null && !currentName.isEmpty()) {
                      startAuth(currentName);
                  } else {
                      showFragment(new SignUpFragment(), false);
                  }
                  break;
              case AUTH_PHONE:
                  currentAuthType = AUTH_TYPE_PHONE;
                  currentCode = "";
                  showFragment(ActorSDK.sharedActor().getDelegatedFragment(ActorSDK.sharedActor().getDelegate().getAuthStartIntent(), new SignPhoneFragment(), BaseAuthFragment.class), false);
                  break;
              case AUTH_EMAIL:
                  currentCode = "";
                  currentAuthType = AUTH_TYPE_EMAIL;
                  showFragment(ActorSDK.sharedActor().getDelegatedFragment(ActorSDK.sharedActor().getDelegate().getAuthStartIntent(), new SignEmailFragment(), BaseAuthFragment.class), false);
                  break;
              case CODE_VALIDATION_PHONE:
              case CODE_VALIDATION_EMAIL:
                  Fragment signInFragment = new ValidateCodeFragment();
                  Bundle args = new Bundle();
  
                  args.putString("authType", state == AuthState.CODE_VALIDATION_EMAIL ? ValidateCodeFragment.AUTH_TYPE_EMAIL : ValidateCodeFragment.AUTH_TYPE_PHONE);
                  args.putBoolean(ValidateCodeFragment.AUTH_TYPE_SIGN, signType == SIGN_TYPE_IN);
                  args.putString("authId", state == AuthState.CODE_VALIDATION_EMAIL ? currentEmail : Long.toString(currentPhone));
                  signInFragment.setArguments(args);
                  showFragment(signInFragment, false);
                  break;
              case LOGGED_IN:
                  finish();
                  ActorSDK.sharedActor().startAfterLoginActivity(this);
                  break;
          }
      }
  
      public void startAuth(String name) {
          currentName = name;
          currentSex = Sex.UNKNOWN;
          availableAuthType = ActorSDK.sharedActor().getAuthType();
          AuthState authState;
          if (!codeValidated) {
              if ((availableAuthType & AUTH_TYPE_PHONE) == AUTH_TYPE_PHONE) {
                  authState = AuthState.AUTH_PHONE;
              } else if ((availableAuthType & AUTH_TYPE_EMAIL) == AUTH_TYPE_EMAIL) {
                  authState = AuthState.AUTH_EMAIL;
              } else {
                  // none of valid auth types selected - force crash?
                  return;
              }
  
              updateState(authState);
  
          } else {
              signUp(messenger().doSignup(currentName, currentSex != null ? currentSex : Sex.UNKNOWN, transactionHash), currentName, currentSex);
          }
      }
  
      public void startPhoneAuth(Promise<AuthStartRes> promise, long phone) {
          currentAuthType = AUTH_TYPE_PHONE;
          currentPhone = phone;
          startAuth(promise);
      }
  
      public void startEmailAuth(Promise<AuthStartRes> promise, String email) {
          currentAuthType = AUTH_TYPE_EMAIL;
          currentEmail = email;
          startAuth(promise);
      }
  
      private void startAuth(Promise<AuthStartRes> res) {
          showProgress();
          res.then(new Consumer<AuthStartRes>() {
              @Override
              public void apply(AuthStartRes authStartRes) {
                  if (dismissProgress()) {
                      transactionHash = authStartRes.getTransactionHash();
                      isRegistered = authStartRes.isRegistered();
                      switch (authStartRes.getAuthMode()) {
                          case OTP:
                              switch (currentAuthType) {
                                  case AUTH_TYPE_PHONE:
                                      updateState(AuthState.CODE_VALIDATION_PHONE);
                                      break;
  
                                  case AUTH_TYPE_EMAIL:
                                      updateState(AuthState.CODE_VALIDATION_EMAIL);
                                      break;
                              }
                              break;
  
                          default:
                              //not supported AuthMode - force crash?
                      }
                  }
              }
          }).failure(new Consumer<Exception>() {
              @Override
              public void apply(Exception e) {
                  handleAuthError(e);
              }
          });
      }
  
  
      public void validateCode(Promise<AuthCodeRes> promise, String code) {
          currentCode = code;
          showProgress();
          promise.then(new Consumer<AuthCodeRes>() {
              @Override
              public void apply(AuthCodeRes authCodeRes) {
                  if (dismissProgress()) {
                      codeValidated = true;
                      transactionHash = authCodeRes.getTransactionHash();
                      if (!authCodeRes.isNeedToSignup()) {
                          messenger().doCompleteAuth(authCodeRes.getResult()).then(new Consumer<Boolean>() {
                              @Override
                              public void apply(Boolean aBoolean) {
                                  updateState(AuthState.LOGGED_IN);
                              }
                          }).failure(new Consumer<Exception>() {
                              @Override
                              public void apply(Exception e) {
                                  handleAuthError(e);
                              }
                          });
                      } else {
                          if (currentName == null || currentName.isEmpty()) {
                              updateState(AuthState.SIGN_UP, true);
                          } else {
                              signUp(messenger().doSignup(currentName, currentSex != null ? currentSex : Sex.UNKNOWN, transactionHash), currentName, currentSex);
                          }
                      }
                  }
              }
          }).failure(new Consumer<Exception>() {
              @Override
              public void apply(Exception e) {
                  handleAuthError(e);
              }
          });
      }
  
      public void signUp(Promise<AuthRes> promise, String name, Sex sex) {
          currentName = name;
          currentSex = sex;
          promise.then(new Consumer<AuthRes>() {
              @Override
              public void apply(AuthRes authRes) {
                  dismissProgress();
                  messenger().doCompleteAuth(authRes).then(new Consumer<Boolean>() {
                      @Override
                      public void apply(Boolean aBoolean) {
                          updateState(AuthState.LOGGED_IN);
                      }
                  }).failure(new Consumer<Exception>() {
                      @Override
                      public void apply(Exception e) {
                          handleAuthError(e);
                      }
                  });
  
              }
          }).failure(new Consumer<Exception>() {
              @Override
              public void apply(Exception e) {
                  handleAuthError(e);
              }
          });
      }
  
      public void handleAuthError(final Exception e) {
          runOnUiThread(new Runnable() {
              @Override
              public void run() {
                  if (dismissProgress()) {
                      boolean canTryAgain = false;
                      boolean keepState = false;
                      String message = getString(R.string.error_unknown);
                      String tag = "UNKNOWN";
                      if (e instanceof RpcException) {
                          RpcException re = (RpcException) e;
                          if (re instanceof RpcInternalException) {
                              message = getString(R.string.error_unknown);
                              canTryAgain = true;
                          } else if (re instanceof RpcTimeoutException) {
                              message = getString(R.string.error_connection);
                              canTryAgain = true;
                          } else {
                              if ("PHONE_CODE_EXPIRED".equals(re.getTag()) || "EMAIL_CODE_EXPIRED".equals(re.getTag())) {
                                  currentCode = "";
                                  message = getString(R.string.auth_error_code_expired);
                                  canTryAgain = false;
                              } else if ("PHONE_CODE_INVALID".equals(re.getTag()) || "EMAIL_CODE_INVALID".equals(re.getTag())) {
                                  message = getString(R.string.auth_error_code_invalid);
                                  canTryAgain = false;
                                  keepState = true;
                              } else if ("FAILED_GET_OAUTH2_TOKEN".equals(re.getTag())) {
                                  message = getString(R.string.auth_error_failed_get_oauth2_token);
                                  canTryAgain = false;
                              } else {
                                  message = re.getMessage();
                                  canTryAgain = re.isCanTryAgain();
                              }
                          }
                      }
  
  
                      try {
                          if (canTryAgain) {
                              new AlertDialog.Builder(AuthActivity.this)
                                      .setMessage(message)
                                      .setPositiveButton(R.string.dialog_try_again, new DialogInterface.OnClickListener() {
                                          @Override
                                          public void onClick(DialogInterface dialog, int which) {
                                              dismissAlert();
                                              switch (state) {
                                                  case AUTH_EMAIL:
                                                  case AUTH_PHONE:
                                                      switch (currentAuthType) {
                                                          case AUTH_TYPE_PHONE:
                                                              startAuth(messenger().doStartPhoneAuth(currentPhone));
                                                              break;
  
                                                          case AUTH_TYPE_EMAIL:
                                                              startAuth(messenger().doStartEmailAuth(currentEmail));
  
                                                              break;
                                                      }
                                                      break;
                                                  case CODE_VALIDATION_EMAIL:
                                                  case CODE_VALIDATION_PHONE:
                                                      validateCode(messenger().doValidateCode(currentCode, transactionHash), currentCode);
                                                      break;
  
                                                  case SIGN_UP:
                                                      signUp(messenger().doSignup(currentName, currentSex!=null?currentSex:Sex.UNKNOWN, transactionHash), currentName, currentSex);
                                                      break;
                                              }
  
                                          }
                                      })
                                      .setNegativeButton(R.string.dialog_cancel, new DialogInterface.OnClickListener() {
                                          @Override
                                          public void onClick(DialogInterface dialog, int which) {
                                              dismissAlert();
                                              updateState(AuthState.AUTH_START);
                                          }
                                      }).setCancelable(false)
                                      .show()
                                      .setCanceledOnTouchOutside(false);
                          } else {
                              final boolean finalKeepState = keepState;
                              new AlertDialog.Builder(AuthActivity.this)
                                      .setMessage(message)
                                      .setPositiveButton(R.string.dialog_ok, new DialogInterface.OnClickListener() {
                                          @Override
                                          public void onClick(DialogInterface dialog, int which) {
                                              dismissAlert();
                                              if (finalKeepState) {
                                                  updateState(state, true);
                                              } else if (signType == SIGN_TYPE_UP) {
                                                  if (currentAuthType == AUTH_TYPE_EMAIL) {
                                                      switchToEmailAuth();
                                                  } else if (currentAuthType == AUTH_TYPE_PHONE) {
                                                      switchToPhoneAuth();
                                                  } else {
                                                      updateState(AuthState.AUTH_START);
                                                  }
                                              } else if (signType == SIGN_TYPE_IN) {
                                                  startSignIn();
                                              } else {
                                                  updateState(AuthState.AUTH_START);
                                              }
  
                                          }
                                      })
                                      .setCancelable(false)
                                      .show()
                                      .setCanceledOnTouchOutside(false);
                          }
                      } catch (Exception ex) {
                          ex.printStackTrace();
                      }
                  }
              }
          });
  
      }
  
      public void switchToEmailAuth() {
          updateState(AuthState.AUTH_EMAIL);
      }
  
      public void switchToPhoneAuth() {
          updateState(AuthState.AUTH_PHONE);
      }
  
  
      public void startSignIn() {
          signType = SIGN_TYPE_IN;
          updateState(AuthState.AUTH_START, true);
      }
  
  
      public void startSignUp() {
          signType = SIGN_TYPE_UP;
          updateState(AuthState.AUTH_START, true);
      }
  
  
      public void showProgress() {
          dismissProgress();
          progressDialog = new ProgressDialog(this);
          progressDialog.setCanceledOnTouchOutside(false);
          progressDialog.setCancelable(false);
          progressDialog.setTitle(getString(R.string.progress_common));
          progressDialog.show();
      }
  
      @Override
      protected void onPause() {
          super.onPause();
  
          dismissProgress();
          dismissAlert();
      }
  
      private boolean dismissProgress() {
          if (progressDialog != null) {
              progressDialog.dismiss();
              progressDialog = null;
              return true;
          }
          return false;
      }
  
      private void dismissAlert() {
          if (alertDialog != null) {
              alertDialog.dismiss();
              alertDialog = null;
          }
      }
  
      public String getCurrentCode() {
          return currentCode;
      }
  
      public String getTransactionHash() {
          return transactionHash;
      }
  }
  
  
  
  