/* * Copyright (c) 2014. The Trustees of Indiana University. * * This version of the code is licensed under the MPL 2.0 Open Source license with additional * healthcare disclaimer. If the user is an entity intending to commercialize any application * that uses this code in a for-profit venture, please contact the copyright holder. */ package com.muzima.view.login; import android.app.Activity; import android.app.AlertDialog; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Typeface; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import android.animation.ValueAnimator; import com.muzima.MuzimaApplication; import com.muzima.R; import com.muzima.api.context.Context; import com.muzima.domain.Credentials; import com.muzima.service.CredentialsPreferenceService; import com.muzima.service.LocalePreferenceService; import com.muzima.service.MuzimaSyncService; import com.muzima.service.WizardFinishPreferenceService; import com.muzima.utils.StringUtils; import com.muzima.view.MainActivity; import com.muzima.view.cohort.CohortWizardActivity; import com.muzima.view.setupconfiguration.SetupMethodPreferenceWizardActivity; import java.util.Locale; import static com.muzima.utils.Constants.DataSyncServiceConstants.SyncStatusConstants; //This class shouldn't extend BaseActivity. Since it is independent of the application's context public class LoginActivity extends Activity { private static final String TAG = "LoginActivity"; public static final String isFirstLaunch = "isFirstLaunch"; public static final String sessionTimeOut = "SessionTimeOut"; private EditText serverUrlText; private EditText usernameText; private EditText passwordText; private Button loginButton; private CheckBox updatePassword; private TextView versionText; private BackgroundAuthenticationTask backgroundAuthenticationTask; private TextView authenticatingText; private ValueAnimator flipFromNoConnToLoginAnimator; private ValueAnimator flipFromLoginToNoConnAnimator; private ValueAnimator flipFromLoginToAuthAnimator; private ValueAnimator flipFromAuthToLoginAnimator; private ValueAnimator flipFromAuthToNoConnAnimator; private boolean isUpdatePasswordChecked; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ((MuzimaApplication) getApplication()).cancelTimer(); setContentView(R.layout.activity_login); showSessionTimeOutPopUpIfNeeded(); initViews(); setupListeners(); initAnimators(); boolean isFirstLaunch = getIntent().getBooleanExtra(LoginActivity.isFirstLaunch, true); String serverURL = getServerURL(); if (!isFirstLaunch && !StringUtils.isEmpty(serverURL)) { removeServerUrlAsInput(); } useSavedServerUrl(serverURL); if(isFirstLaunch){ removeChangedPasswordRecentlyCheckbox(); } //Hack to get it to use default font space. passwordText.setTypeface(Typeface.DEFAULT); versionText.setText(getApplicationVersion()); } private void showSessionTimeOutPopUpIfNeeded() { if (getIntent().getBooleanExtra(LoginActivity.sessionTimeOut, false)) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder .setCancelable(true) .setIcon(getResources().getDrawable(R.drawable.ic_warning)) .setTitle(getResources().getString(R.string.general_alert)) .setMessage(getResources().getString(R.string.info_session_time_out)) .setPositiveButton(R.string.general_ok, null).show(); } } private void removeServerUrlAsInput() { serverUrlText.setVisibility(View.GONE); findViewById(R.id.server_url_divider).setVisibility(View.GONE); } private void useSavedServerUrl(String serverUrl) { if (!StringUtils.isEmpty(serverUrl)) { serverUrlText.setText(serverUrl); } } private void removeChangedPasswordRecentlyCheckbox() { updatePassword.setVisibility(View.GONE); } private String getApplicationVersion() { String versionText = ""; String versionCode = ""; try { versionCode = String.valueOf(getPackageManager().getPackageInfo(getPackageName(), 0).versionName); versionText = getResources().getString(R.string.general_application_version, versionCode); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "Unable to read application version.", e); } return versionText; } private String getServerURL() { Credentials credentials; credentials = new Credentials(this); return credentials.getServerUrl(); } @Override public void onResume() { super.onResume(); setupStatusView(); } private void setupStatusView() { if (backgroundAuthenticationTask != null && backgroundAuthenticationTask.getStatus() == AsyncTask.Status.RUNNING) { loginButton.setVisibility(View.GONE); authenticatingText.setVisibility(View.VISIBLE); } else { loginButton.setVisibility(View.VISIBLE); authenticatingText.setVisibility(View.GONE); } } @Override protected void onDestroy() { super.onDestroy(); if (backgroundAuthenticationTask != null) { backgroundAuthenticationTask.cancel(true); } } @Override public void onBackPressed() { moveTaskToBack(true); } private void setupListeners() { loginButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (validInput()) { if (backgroundAuthenticationTask != null && backgroundAuthenticationTask.getStatus() == AsyncTask.Status.RUNNING) { Toast.makeText(getApplicationContext(), getString(R.string.info_authentication_in_progress), Toast.LENGTH_SHORT).show(); return; } String username = (usernameText.getText() == null) ? "" : usernameText.getText().toString().trim(); //not trimming passwords since passwords may contain space. String password = (passwordText.getText() == null) ? "" : passwordText.getText().toString(); backgroundAuthenticationTask = new BackgroundAuthenticationTask(); backgroundAuthenticationTask.execute( new Credentials(serverUrlText.getText().toString(), username, password) ); } else { int errorColor = getResources().getColor(R.color.error_text_color); if (StringUtils.isEmpty(serverUrlText.getText().toString())) { serverUrlText.setHint(getString(R.string.hint_server_url_prompt)); serverUrlText.setHintTextColor(errorColor); } if (StringUtils.isEmpty(usernameText.getText().toString())) { usernameText.setHint(getString(R.string.hint_username_prompt)); usernameText.setHintTextColor(errorColor); } if (StringUtils.isEmpty(passwordText.getText().toString())) { passwordText.setHint(getString(R.string.hint_password_prompt)); passwordText.setHintTextColor(errorColor); } } } }); } private boolean validInput() { return !(StringUtils.isEmpty(serverUrlText.getText().toString()) || StringUtils.isEmpty(usernameText.getText().toString()) || StringUtils.isEmpty(passwordText.getText().toString())); } private void initViews() { serverUrlText = (EditText) findViewById(R.id.serverUrl); usernameText = (EditText) findViewById(R.id.username); passwordText = (EditText) findViewById(R.id.password); updatePassword = (CheckBox) findViewById(R.id.update_password); loginButton = (Button) findViewById(R.id.login); authenticatingText = (TextView) findViewById(R.id.authenticatingText); versionText = (TextView) findViewById(R.id.version); } public void onUpdatePasswordCheckboxClicked(View view) { isUpdatePasswordChecked = ((CheckBox) view).isChecked(); } public void removeRemnantDataFromPreviousRunOfWizard() { if (!new WizardFinishPreferenceService(this).isWizardFinished()) { try { MuzimaApplication application = ((MuzimaApplication) getApplicationContext()); Context context = application.getMuzimaContext(); //Cohort Wizard activity application.getPatientController().deleteAllPatients(); application.getCohortController().deleteCohortMembers(application.getCohortController().getAllCohorts()); application.getCohortController().deleteAllCohorts(); context.getLastSyncTimeService().deleteAll(); //FormTemplateWizardActivity application.getConceptController().deleteAllConcepts(); application.getLocationController().deleteAllLocations(); application.getProviderController().deleteAllProviders(); application.getFormController().deleteAllForms(); application.getFormController().deleteAllFormTemplates(); //CustomConceptWizardActivity context.getObservationService().deleteAll(); context.getEncounterService().deleteAll(); } catch (Throwable e) { Log.e(TAG, "Unable to delete previous wizard run data. Error: " + e); } } } private class BackgroundAuthenticationTask extends AsyncTask<Credentials, Void, BackgroundAuthenticationTask.Result> { @Override protected void onPreExecute() { if (loginButton.getVisibility() == View.VISIBLE) { flipFromLoginToAuthAnimator.start(); } } @Override protected Result doInBackground(Credentials... params) { Credentials credentials = params[0]; MuzimaSyncService muzimaSyncService = ((MuzimaApplication) getApplication()).getMuzimaSyncService(); int authenticationStatus = muzimaSyncService.authenticate(credentials.getCredentialsArray(), isUpdatePasswordChecked); return new Result(credentials, authenticationStatus); } @Override protected void onPostExecute(Result result) { if (result.status == SyncStatusConstants.AUTHENTICATION_SUCCESS) { new CredentialsPreferenceService(getApplicationContext()).saveCredentials(result.credentials); ((MuzimaApplication) getApplication()).restartTimer(); LocalePreferenceService localePreferenceService = ((MuzimaApplication) getApplication()).getLocalePreferenceService(); String currentLocale = Locale.getDefault().toString(); localePreferenceService.setPreferredLocale(currentLocale); startNextActivity(); } else { Toast.makeText(getApplicationContext(), getErrorText(result), Toast.LENGTH_SHORT).show(); if (authenticatingText.getVisibility() == View.VISIBLE || flipFromLoginToAuthAnimator.isRunning()) { flipFromLoginToAuthAnimator.cancel(); flipFromAuthToLoginAnimator.start(); } } } private String getErrorText(Result result) { switch (result.status) { case SyncStatusConstants.MALFORMED_URL_ERROR: return getString(R.string.error_server_url_invalid); case SyncStatusConstants.INVALID_CREDENTIALS_ERROR: return getString(R.string.error_credential_invalid); case SyncStatusConstants.INVALID_CHARACTER_IN_USERNAME: return getString(R.string.error_username_invalid_format) + SyncStatusConstants.INVALID_CHARACTER_FOR_USERNAME; case SyncStatusConstants.LOCAL_CONNECTION_ERROR: return getString(R.string.error_local_connection_unavailable); case SyncStatusConstants.SERVER_CONNECTION_ERROR: return getString(R.string.error_server_connection_unavailable); default: return getString(R.string.error_authentication_fail); } } private void startNextActivity() { Intent intent; if (new WizardFinishPreferenceService(LoginActivity.this).isWizardFinished()) { intent = new Intent(getApplicationContext(), MainActivity.class); } else { removeRemnantDataFromPreviousRunOfWizard(); intent = new Intent(getApplicationContext(), SetupMethodPreferenceWizardActivity.class); } startActivity(intent); finish(); } protected class Result { Credentials credentials; int status; private Result(Credentials credentials, int status) { this.credentials = credentials; this.status = status; } } } private void initAnimators() { flipFromLoginToNoConnAnimator = ValueAnimator.ofFloat(0, 1); flipFromNoConnToLoginAnimator = ValueAnimator.ofFloat(0, 1); flipFromLoginToAuthAnimator = ValueAnimator.ofFloat(0, 1); flipFromAuthToLoginAnimator = ValueAnimator.ofFloat(0, 1); flipFromAuthToNoConnAnimator = ValueAnimator.ofFloat(0, 1); initFlipAnimation(flipFromLoginToAuthAnimator, loginButton, authenticatingText); initFlipAnimation(flipFromAuthToLoginAnimator, authenticatingText, loginButton); } public void initFlipAnimation(ValueAnimator valueAnimator, final View from, final View to) { valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); valueAnimator.setDuration(300); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float animatedFraction = animation.getAnimatedFraction(); if (from.getVisibility() == View.VISIBLE) { if (animatedFraction > 0.5) { from.setVisibility(View.INVISIBLE); to.setVisibility(View.VISIBLE); } } else if (to.getVisibility() == View.VISIBLE) { to.setRotationX(-180 * (1 - animatedFraction)); } if (from.getVisibility() == View.VISIBLE) { from.setRotationX(180 * animatedFraction); } } }); } }