Java tutorial
/** * Apow - a mobile EHR Management System for low-resource environments * in developing countries, exemplified by rural Ghana * Copyright (C) 2014 Martin Landua * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ package de.uni_koblenz_landau.apow; import java.util.ArrayList; import java.util.List; import de.uni_koblenz_landau.apow.dialogs.SignedOffDialog; import de.uni_koblenz_landau.apow.helper.Constants; import de.uni_koblenz_landau.apow.helper.CustomActivity; import de.uni_koblenz_landau.apow.helper.Helper; import de.uni_koblenz_landau.apow.helper.ListViewItem; import de.uni_koblenz_landau.apow.tasks.LocationWebInterface; import de.uni_koblenz_landau.apow.tasks.LocationWebTask; import de.uni_koblenz_landau.apow.tasks.LoginInterface; import de.uni_koblenz_landau.apow.tasks.LoginTask; import de.uni_koblenz_landau.apow.tasks.NFCTagReaderInterface; import de.uni_koblenz_landau.apow.tasks.NFCTagReaderTask; import de.uni_koblenz_landau.apow.tasks.SetupInterface; import de.uni_koblenz_landau.apow.tasks.SetupTask; import de.uni_koblenz_landau.apow.tasks.TaskActivity; import de.uni_koblenz_landau.apow.tasks.TaskActivityReference; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.Activity; import android.app.PendingIntent; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentFilter.MalformedMimeTypeException; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.os.Build; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.view.KeyEvent; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.ArrayAdapter; import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; /** * Activity for login * * @author Martin Landua * * Sources: * http://code.tutsplus.com/tutorials/reading-nfc-tags-with-android--mobile-17278 */ public class LoginActivity extends CustomActivity implements TaskActivity, LoginInterface, SetupInterface, LocationWebInterface, NFCTagReaderInterface { // Constants private static final String ARG_AUTO_SIGN_OFF_DISMISSED = "auto_sign_off_dismissed"; private static final String ARG_NFC = "nfc"; private static final String ARG_NFC_STATE = "nfc_state"; private static final String ARG_FIRST_RUN = "first_run"; private static final String ARG_LOCATIONS = "locations"; private static final String ARG_LOCATION_SELECTION = "location_selection"; private static final String ARG_PROGRESS = "progress"; public static final String ARG_AUTO_LOGOUT = "auto_logout"; private static final String SIGNED_OFF_DIALOG_ID = "signed_off_dialog"; private static final String MIME_TEXT_PLAIN = "text/plain"; public static final int STATUS_DATABASE = 1; public static final int STATUS_DATABASE_ERROR = 2; public static final int STATUS_SYNC_FETCH_FACTS = 3; public static final int STATUS_SYNC_FETCH_USERDATA = 4; public static final int STATUS_CONNECTION_ERROR = 5; public static final int STATUS_OK = 6; public static final int STATUS_CHECK_LOGIN = 7; public static final int STATUS_LOGIN_WRONG = 8; // Tasks private LoginTask mLoginTask = null; private SetupTask mSetupTask = null; private NFCTagReaderTask mNFCTagReaderTask = null; private LocationWebTask mLocationWebTask = null; // UI Variables private Boolean mAutoSignOffDismissed; private String mNFCTag; private String mLoginPassword; private String mDatabasePassword; private String mDatabaseConfirm; private String mServerUsername; private String mServerPassword; private int mLocationID; private Boolean mFirstRun; private int mNFCTextDrawable; private NfcAdapter mNfcAdapter; private Boolean mProgress; private List<ListViewItem> mLocations; // UI references. private Toolbar mToolbar; private View mLoginFormView; private EditText mLoginPasswordView; private TextView mLoginNFCText; private View mSetupFormView; private TextView mSetupNFCText; private EditText mSetupDatabasePasswordView; private EditText mSetupDatabaseConfirmView; private EditText mSetupServerUsernameView; private EditText mSetupServerPasswordView; private Spinner mSetupLocationView; private View mStatusView; private TextView mStatusMessageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Re-attach Tasks. TaskActivityReference.getInstance().attach(LoginTask.TASK_ID, this); TaskActivityReference.getInstance().attach(SetupTask.TASK_ID, this); TaskActivityReference.getInstance().attach(NFCTagReaderTask.TASK_ID, this); TaskActivityReference.getInstance().attach(LocationWebTask.TASK_ID, this); // Create UI references. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = this.getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); window.setStatusBarColor(this.getResources().getColor(R.color.color_primary_dark)); } setContentView(R.layout.login_activity); mToolbar = (Toolbar) findViewById(R.id.toolbar); if (mToolbar != null) { setSupportActionBar(mToolbar); } mSetupLocationView = (Spinner) findViewById(R.id.login_setup_locations); mStatusView = findViewById(R.id.login_status); mStatusMessageView = (TextView) findViewById(R.id.login_status_message); mLoginFormView = findViewById(R.id.login_login); mLoginPasswordView = (EditText) findViewById(R.id.login_login_password); mLoginPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) { if (id == R.id.login || id == EditorInfo.IME_NULL) { login(textView); return true; } return false; } }); mLoginNFCText = (TextView) findViewById(R.id.login_login_nfc); mSetupFormView = findViewById(R.id.login_setup); mSetupNFCText = (TextView) findViewById(R.id.login_setup_nfc); mSetupDatabasePasswordView = (EditText) findViewById(R.id.login_setup_database_password); mSetupDatabaseConfirmView = (EditText) findViewById(R.id.login_setup_database_password_confirm); mSetupServerUsernameView = (EditText) findViewById(R.id.login_login_server_user); mSetupServerPasswordView = (EditText) findViewById(R.id.login_login_server_password); //mFirstRun = true; mFirstRun = getSharedPreferences(Constants.PREFERENCE, MODE_PRIVATE).getBoolean(ARG_FIRST_RUN, true); mNfcAdapter = NfcAdapter.getDefaultAdapter(this); // Restore UI from saved instance or load data. if (savedInstanceState != null) { mNFCTag = savedInstanceState.getString(ARG_NFC); mNFCTextDrawable = savedInstanceState.getInt(ARG_NFC_STATE); mAutoSignOffDismissed = savedInstanceState.getBoolean(ARG_AUTO_SIGN_OFF_DISMISSED); mProgress = savedInstanceState.getBoolean(ARG_PROGRESS); if (mFirstRun) { mLocations = savedInstanceState.getParcelableArrayList(ARG_LOCATIONS); if (mLocations != null) { ArrayAdapter<ListViewItem> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, mLocations); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mSetupLocationView.setAdapter(adapter); mSetupLocationView.setSelection(savedInstanceState.getInt(ARG_LOCATION_SELECTION)); } } } else { mProgress = false; mAutoSignOffDismissed = false; mNFCTag = ""; mNFCTextDrawable = R.drawable.empty; if (mFirstRun) { loadLocations(); } } mLoginNFCText.setCompoundDrawablesWithIntrinsicBounds(0, 0, mNFCTextDrawable, 0); mSetupNFCText.setCompoundDrawablesWithIntrinsicBounds(0, 0, mNFCTextDrawable, 0); if (mFirstRun) { mSetupFormView.setVisibility(View.VISIBLE); mLoginFormView.setVisibility(View.GONE); this.setTitle(R.string.login_setup_title); } else { mSetupFormView.setVisibility(View.GONE); mLoginFormView.setVisibility(View.VISIBLE); this.setTitle(R.string.login_login_title); } if (getIntent().getBooleanExtra(ARG_AUTO_LOGOUT, false) && !mAutoSignOffDismissed) { SignedOffDialog dialog = new SignedOffDialog(); dialog.show(getSupportFragmentManager(), SIGNED_OFF_DIALOG_ID); mAutoSignOffDismissed = true; } showProgress(mProgress); setNFCState(); } @Override protected void onDestroy() { super.onDestroy(); // Detach tasks for preventing memory leaks. TaskActivityReference.getInstance().detach(LoginTask.TASK_ID); TaskActivityReference.getInstance().detach(SetupTask.TASK_ID); TaskActivityReference.getInstance().detach(NFCTagReaderTask.TASK_ID); TaskActivityReference.getInstance().detach(LocationWebTask.TASK_ID); } @Override protected void onSaveInstanceState(Bundle savedInstanceState) { super.onSaveInstanceState(savedInstanceState); savedInstanceState.putString(ARG_NFC, mNFCTag); savedInstanceState.putInt(ARG_NFC_STATE, mNFCTextDrawable); savedInstanceState.putBoolean(ARG_AUTO_SIGN_OFF_DISMISSED, mAutoSignOffDismissed); savedInstanceState.putBoolean(ARG_PROGRESS, mProgress); if (mFirstRun) { savedInstanceState.putParcelableArrayList(ARG_LOCATIONS, (ArrayList<ListViewItem>) mLocations); savedInstanceState.putInt(ARG_LOCATION_SELECTION, mSetupLocationView.getSelectedItemPosition()); } } @Override protected void onNewIntent(Intent intent) { setIntent(intent); String action = intent.getAction(); // If tag is detected, start reading it. if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action) && MIME_TEXT_PLAIN.equals(intent.getType())) { Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); readNFCTag(tag); } } /** * Start task to read a NFC tag * @param tag Tag */ private void readNFCTag(Tag tag) { // Abort if task is already running. if (mNFCTagReaderTask != null) { return; } mLoginNFCText.setError(null); mSetupNFCText.setError(null); mNFCTextDrawable = R.drawable.empty; mLoginNFCText.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.empty, 0); mSetupNFCText.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.empty, 0); mNFCTagReaderTask = new NFCTagReaderTask(); TaskActivityReference.getInstance().addTask(NFCTagReaderTask.TASK_ID, mNFCTagReaderTask, this); mNFCTagReaderTask.execute(tag); } /** * Called by NFCTagReaderTask, when reading is finished. */ @Override public void onNFCTagReaderFinished(String result) { mNFCTagReaderTask = null; mNFCTag = result; mLoginNFCText.setError(null); mSetupNFCText.setError(null); mNFCTextDrawable = R.drawable.ic_action_accept_light; mLoginNFCText.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); mSetupNFCText.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); mLoginNFCText.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_action_accept_light, 0); mSetupNFCText.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_action_accept_light, 0); } @Override protected void onResume() { super.onResume(); // Start scanning for NFC tags. setupForegroundDispatch(this, mNfcAdapter); setNFCState(); } /** * Start scanning for NFC tags. * @param activity The Activity requesting to the foreground dispatch. * @param adapter The NfcAdapter used for the foreground dispatch. */ private static void setupForegroundDispatch(final Activity activity, NfcAdapter adapter) { if (adapter != null) { // Add intents for NFC. final Intent intent = new Intent(activity.getApplicationContext(), activity.getClass()); intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); final PendingIntent pendingIntent = PendingIntent.getActivity(activity.getApplicationContext(), 0, intent, 0); IntentFilter[] filters = new IntentFilter[1]; String[][] techList = new String[][] {}; filters[0] = new IntentFilter(); filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED); filters[0].addCategory(Intent.CATEGORY_DEFAULT); try { filters[0].addDataType(MIME_TEXT_PLAIN); } catch (MalformedMimeTypeException e) { e.printStackTrace(); } adapter.enableForegroundDispatch(activity, pendingIntent, filters, techList); } } /** * Sets NFC status view. */ private void setNFCState() { if (mNfcAdapter != null) { // If NFC is not enabled, show message. if (mNfcAdapter.isEnabled()) { if (mNFCTextDrawable == R.drawable.ic_action_error) { mNFCTextDrawable = R.drawable.empty; mLoginNFCText.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.empty, 0); mSetupNFCText.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.empty, 0); } mLoginNFCText.setText(R.string.login_nfc); mSetupNFCText.setText(R.string.login_nfc); } else { mLoginNFCText.setText(R.string.login_nfc_disabled); mSetupNFCText.setText(R.string.login_nfc_disabled); mNFCTextDrawable = R.drawable.ic_action_error; mLoginNFCText.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_action_error, 0); mSetupNFCText.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_action_error, 0); } } } @Override protected void onPause() { // Stop scanning for NFC tags. stopForegroundDispatch(this, mNfcAdapter); super.onPause(); } /** * Stop scanning for NFC tags. * @param activity The Activity requesting to the foreground dispatch. * @param adapter The NfcAdapter used for the foreground dispatch. */ private static void stopForegroundDispatch(final Activity activity, NfcAdapter adapter) { if (adapter != null) { adapter.disableForegroundDispatch(activity); } } /** * Called by onClick of NFC status view. * @param view View */ public void openNFCSettings(View view) { if (mNfcAdapter != null && !mNfcAdapter.isEnabled()) { startActivity(new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS)); } } /** * Checks input and logs in. */ public void login(View view) { // Abort if task is already running. if (mLoginTask != null || mFirstRun) { return; } // Reset errors. mLoginPasswordView.setError(null); // Store UI values. mLoginPassword = mLoginPasswordView.getText().toString(); boolean cancel = false; View focusView = null; // Check input. if (TextUtils.isEmpty(mLoginPassword)) { mLoginPasswordView.setError(getString(R.string.error_field_required)); focusView = mLoginPasswordView; cancel = true; } else if (mLoginPassword.length() < 4) { mLoginPasswordView.setError(getString(R.string.error_invalid_password)); focusView = mLoginPasswordView; cancel = true; } if (TextUtils.isEmpty(mNFCTag)) { mLoginNFCText.setError(getString(R.string.error_no_tag)); focusView = mLoginNFCText; cancel = true; } // When there are no error start login task. if (cancel) { focusView.requestFocus(); } else { mStatusMessageView.setText(R.string.login_login_progress); showProgress(true); mLoginTask = new LoginTask(); TaskActivityReference.getInstance().addTask(LoginTask.TASK_ID, mLoginTask, this); mLoginTask.execute(Helper.hash(mNFCTag + mLoginPassword)); } } /** * Called by login task, when login is finished. */ @Override public void onLoginFinished(Boolean success) { mLoginTask = null; showProgress(false); // Leave activity. if (success) { Intent intent = new Intent(this, PatientListActivity.class); startActivity(intent); // Start automatic logout timer. getApp().startWaiter(); finish(); } else { // Show error. Toast.makeText(this, R.string.login_login_error, Toast.LENGTH_SHORT).show(); } } /** * Checks input and setups database. */ public void setup(View view) { if (mSetupTask != null || !mFirstRun) { return; } // Reset errors. mSetupDatabasePasswordView.setError(null); mSetupDatabaseConfirmView.setError(null); mSetupServerUsernameView.setError(null); mSetupServerPasswordView.setError(null); // Store UI values. mDatabasePassword = mSetupDatabasePasswordView.getText().toString(); mDatabaseConfirm = mSetupDatabaseConfirmView.getText().toString(); mServerUsername = mSetupServerUsernameView.getText().toString(); mServerPassword = mSetupServerPasswordView.getText().toString(); Boolean cancel = false; View focusView = null; // Check input. if (mSetupLocationView.getAdapter() != null) { mLocationID = ((ListViewItem) mSetupLocationView.getSelectedItem()).getId(); } else { Toast.makeText(this, R.string.error_connection, Toast.LENGTH_SHORT).show(); focusView = mSetupLocationView; cancel = true; } if (TextUtils.isEmpty(mDatabasePassword)) { mSetupDatabasePasswordView.setError(getString(R.string.error_field_required)); focusView = mSetupDatabasePasswordView; cancel = true; } else if (mDatabasePassword.length() < 4) { mSetupDatabasePasswordView.setError(getString(R.string.error_invalid_password)); focusView = mSetupDatabasePasswordView; cancel = true; } if (TextUtils.isEmpty(mDatabaseConfirm)) { mSetupDatabaseConfirmView.setError(getString(R.string.error_field_required)); focusView = mSetupDatabaseConfirmView; cancel = true; } else if (!mDatabaseConfirm.equals(mDatabasePassword)) { mSetupDatabaseConfirmView.setError(getString(R.string.error_confirm_password)); focusView = mSetupDatabaseConfirmView; cancel = true; } if (TextUtils.isEmpty(mServerUsername)) { mSetupServerUsernameView.setError(getString(R.string.error_field_required)); focusView = mSetupServerUsernameView; cancel = true; } if (TextUtils.isEmpty(mServerPassword)) { mSetupServerPasswordView.setError(getString(R.string.error_field_required)); focusView = mSetupServerPasswordView; cancel = true; } if (TextUtils.isEmpty(mNFCTag)) { mSetupNFCText.setError(getString(R.string.error_no_tag)); focusView = mSetupNFCText; cancel = true; } // When there are no error start login task. if (cancel) { focusView.requestFocus(); } else { mStatusMessageView.setText(R.string.login_setup_progress); showProgress(true); mSetupTask = new SetupTask(Helper.hash(mNFCTag + mDatabasePassword), mServerUsername, mServerPassword, mLocationID); TaskActivityReference.getInstance().addTask(SetupTask.TASK_ID, mSetupTask, this); mSetupTask.execute(); } } /** * Called by setup task, when setup is finished. */ @Override public void onSetupFinished(Integer status) { mSetupTask = null; showProgress(false); if (status == STATUS_OK) { // Set flag for successful initialization. getSharedPreferences(Constants.PREFERENCE, MODE_PRIVATE).edit().putBoolean(ARG_FIRST_RUN, false) .commit(); // Leave activity. Intent intent = new Intent(this, PatientListActivity.class); startActivity(intent); // Start automatic logout timer. getApp().startWaiter(); finish(); // Show errors. } else if (status == STATUS_DATABASE_ERROR) { Toast.makeText(this, R.string.login_setup_error, Toast.LENGTH_SHORT).show(); } else if (status == STATUS_CONNECTION_ERROR) { Toast.makeText(this, R.string.error_connection, Toast.LENGTH_SHORT).show(); } else if (status == STATUS_LOGIN_WRONG) { Toast.makeText(this, R.string.login_setup_login_wrong, Toast.LENGTH_SHORT).show(); } } // Show progress of setup task. @Override public void updateStatus(int status) { String message = ""; switch (status) { case STATUS_DATABASE: message = getString(R.string.login_setup_status_database); break; case STATUS_CHECK_LOGIN: message = getString(R.string.login_setup_status_check_login); break; case STATUS_SYNC_FETCH_FACTS: message = getString(R.string.login_setup_status_fetch_facts); break; case STATUS_SYNC_FETCH_USERDATA: message = getString(R.string.login_setup_status_sync_fetch_userdata); break; } mStatusMessageView.setText(message); } /** * Shows the progress UI and hides the login form. */ private void showProgress(final boolean show) { mProgress = show; InputMethodManager inputManager = (InputMethodManager) getActivity() .getSystemService(FragmentActivity.INPUT_METHOD_SERVICE); if (getActivity().getCurrentFocus() != null) { inputManager.hideSoftInputFromWindow(getActivity().getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); } int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime); mStatusView.setVisibility(View.VISIBLE); mStatusView.animate().setDuration(shortAnimTime).alpha(show ? 1 : 0) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mStatusView.setVisibility(show ? View.VISIBLE : View.GONE); } }); if (mFirstRun) { mSetupFormView.setVisibility(View.VISIBLE); mSetupFormView.animate().setDuration(shortAnimTime).alpha(show ? 0 : 1) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mSetupFormView.setVisibility(show ? View.GONE : View.VISIBLE); } }); } else { mLoginFormView.setVisibility(View.VISIBLE); mLoginFormView.animate().setDuration(shortAnimTime).alpha(show ? 0 : 1) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); } }); } } @Override public Activity getActivity() { return this; } /** * Fetches locations. */ public void loadLocations() { if (mLocationWebTask != null || mSetupLocationView.getAdapter() != null) { return; } mStatusMessageView.setText(R.string.login_setup_locations_progress); showProgress(true); mLocationWebTask = new LocationWebTask(); TaskActivityReference.getInstance().addTask(LocationWebTask.TASK_ID, mLocationWebTask, this); mLocationWebTask.execute(); } @Override public void onLocationsFetched(List<ListViewItem> result) { mLocationWebTask = null; showProgress(false); if (result != null) { mLocations = result; ArrayAdapter<ListViewItem> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, result); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mSetupLocationView.setAdapter(adapter); } else { // show error Toast.makeText(this, R.string.error_connection, Toast.LENGTH_SHORT).show(); } } }