content: /*
   * 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;
          }
  
      }
  
  }
  