/* * Universal Password Manager * Copyright (c) 2010-2011 Adrian Smith * * This file is part of Universal Password Manager. * * Universal Password Manager 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 2 of the License, or * (at your option) any later version. * * Universal Password Manager 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 Universal Password Manager; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package com.u17od.upm; import java.io.File; import java.io.IOException; import java.security.GeneralSecurityException; import android.app.Activity; import android.app.ProgressDialog; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnKeyListener; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import com.u17od.upm.crypto.InvalidPasswordException; import com.u17od.upm.database.PasswordDatabase; import com.u17od.upm.database.ProblemReadingDatabaseFile; /** * This Activity is responsible for prompting the user to enter their master * password and then decrypting the database. If the correct password is entered * then the AccountList Activity is loaded. */ public class EnterMasterPassword extends Activity implements OnClickListener { public static PasswordDatabase decryptedPasswordDatabase; public static File databaseFileToDecrypt; private EditText passwordField; private DecryptDatabase decryptDatabaseTask; private ProgressDialog progressDialog; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.enter_master_password); passwordField = (EditText) findViewById(R.id.password); passwordField.setText(null); // Make this class the listener for the click event on the OK button Button okButton = (Button) findViewById(R.id.master_password_open_button); okButton.setOnClickListener(this); passwordField.setOnKeyListener(new OnKeyListener() { public boolean onKey(View v, int keyCode, KeyEvent event) { if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { openDatabase(); return true; } return false; } }); decryptDatabaseTask = (DecryptDatabase) getLastNonConfigurationInstance(); if (decryptDatabaseTask != null) { // Associate the async task with the new activity decryptDatabaseTask.setActivity(this); // If the decryptDatabaseTask is running display the progress // dialog. This can happen if the screen was rotated while the // background task is running. if (decryptDatabaseTask.getStatus() == AsyncTask.Status.RUNNING) { progressDialog = ProgressDialog.show(this, "", this.getString(R.string.decrypting_db)); } } } public ProgressDialog getProgressDialog() { return this.progressDialog; } @Override public void onClick(View v) { switch (v.getId()) { case R.id.master_password_open_button: openDatabase(); break; } } @Override protected void onStop() { super.onStop(); // If the activity is being stopped while the progress dialog is // displayed (e.g. the screen is being rotated) dismiss it here. // We'll display it again in the new activity. if (progressDialog != null) { progressDialog.dismiss(); } } @Override public Object onRetainNonConfigurationInstance () { // Disassociate the background task from the activity. A new one will // be created imminently. if (decryptDatabaseTask != null) { decryptDatabaseTask.setActivity(null); } return decryptDatabaseTask; } private void openDatabase() { // Show the progress dialog progressDialog = ProgressDialog.show( this, "", this.getString(R.string.decrypting_db)); // In certain situations (which I'm not clear on) databaseFileToDecrypt // can be null. Check here to ensure we don't end up crashing. if (EnterMasterPassword.databaseFileToDecrypt == null) { Log.w("EnterMasterPassword", "databaseFileToDecrypt was unexpectedly null"); EnterMasterPassword.databaseFileToDecrypt = Utilities.getDatabaseFile(this); } // Create and execute the background task that will decrypt the db decryptDatabaseTask = new DecryptDatabase(this); decryptDatabaseTask.execute(); } public EditText getPasswordField() { return passwordField; } // Show a progress dialog and then start the decrypting of the // db in a separate thread private static class DecryptDatabase extends AsyncTask<Void, Void, Integer> { private static final int ERROR_INVALID_PASSWORD = 1; private static final int ERROR_GENERIC_ERROR = 2; private EnterMasterPassword activity; private String errorMessage; private char[] password; public DecryptDatabase(EnterMasterPassword activity) { this.activity = activity; } @Override protected void onPreExecute() { password = activity.getPasswordField().getText().toString().toCharArray(); } @Override protected Integer doInBackground(Void... params) { int errorCode = 0; try { // Attempt to decrypt the database decryptedPasswordDatabase = new PasswordDatabase(databaseFileToDecrypt, password); } catch (InvalidPasswordException e) { Log.e("EnterMasterPassword", e.getMessage(), e); errorMessage = e.getMessage(); errorCode = ERROR_INVALID_PASSWORD; } catch (IOException e) { Log.e("EnterMasterPassword", e.getMessage(), e); errorMessage = e.getMessage(); errorCode = ERROR_GENERIC_ERROR; } catch (GeneralSecurityException e) { Log.e("EnterMasterPassword", e.getMessage(), e); errorMessage = e.getMessage(); errorCode = ERROR_GENERIC_ERROR; } catch (ProblemReadingDatabaseFile e) { Log.e("EnterMasterPassword", e.getMessage(), e); errorMessage = e.getMessage(); errorCode = ERROR_GENERIC_ERROR; } return errorCode; } @Override protected void onPostExecute(Integer result) { activity.getProgressDialog().dismiss(); switch (result) { case ERROR_INVALID_PASSWORD: Toast toast = Toast.makeText(activity, R.string.invalid_password, Toast.LENGTH_SHORT); toast.show(); // Set focus back to the password and select all characters activity.getPasswordField().requestFocus(); activity.getPasswordField().selectAll(); break; case ERROR_GENERIC_ERROR: String message = String.format(activity.getText(R.string.generic_error_with_message).toString(), errorMessage); UIUtilities.showToast(activity, message, true); break; default : activity.setResult(RESULT_OK); activity.finish(); break; } } private void setActivity(EnterMasterPassword activity) { this.activity = activity; } } }