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 android.app.AlertDialog;
  import android.app.Dialog;
  import android.content.DialogInterface;
  import android.content.DialogInterface.OnClickListener;
  import android.content.Intent;
  import android.content.pm.PackageInfo;
  import android.content.pm.PackageManager.NameNotFoundException;
  import android.net.Uri;
  import android.os.Bundle;
  import android.os.Environment;
  import android.util.Log;
  import android.view.KeyEvent;
  import android.view.LayoutInflater;
  import android.view.Menu;
  import android.view.MenuInflater;
  import android.view.MenuItem;
  import android.view.View;
  import android.widget.ArrayAdapter;
  import android.widget.TextView;
  import android.widget.Toast;
  
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.nio.channels.FileChannel;
  
  
  public class FullAccountList extends AccountsList {
  
      private static final int CONFIRM_RESTORE_DIALOG = 0;
      private static final int CONFIRM_OVERWRITE_BACKUP_FILE = 1;
      private static final int DIALOG_ABOUT = 2;
      private static final int CONFIRM_DELETE_DB_DIALOG = 3;
      private static final int IMPORT_CERT_DIALOG = 4;
  
      public static final int RESULT_EXIT = 0;
      public static final int RESULT_ENTER_PW = 1;
  
      public static final String CERT_FILE_NAME = "upm.cer";
  
      @Override
      public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.main);
          registerForContextMenu(getListView());
          populateAccountList();
      }
  
      @Override
      protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
          switch(requestCode) {
              case AddEditAccount.EDIT_ACCOUNT_REQUEST_CODE:
              case ViewAccountDetails.VIEW_ACCOUNT_REQUEST_CODE:
                  if (resultCode == AddEditAccount.EDIT_ACCOUNT_RESULT_CODE_TRUE) {
                      populateAccountList();
                  }
                  break;
              case SyncDatabaseActivity.SYNC_DB_REQUEST_CODE:
                  if (resultCode == SyncDatabaseActivity.RESULT_REFRESH) {
                      populateAccountList();
                  }
                  break;
          }
      }
  
      private void populateAccountList() {
          if (getPasswordDatabase() == null) {
              // If the UPM process was restarted since AppEntryActivity was last
              // run then databaseFileToDecrypt won't be set so set it here.
              EnterMasterPassword.databaseFileToDecrypt = Utilities.getDatabaseFile(this);
  
              setResult(RESULT_ENTER_PW);
              finish();
          } else {
              setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, getPasswordDatabase().getAccountNames()));
          }
      }
  
      @Override
      public boolean onCreateOptionsMenu(Menu menu) {
          super.onCreateOptionsMenu(menu);
          MenuInflater inflater = getMenuInflater();
          inflater.inflate(R.menu.main, menu);
          return true;
      }
  
      @Override
      public boolean onPrepareOptionsMenu (Menu menu) {
          super.onPrepareOptionsMenu(menu);
          if (getPasswordDatabase() == null) {
              // If the UPM process was restarted since AppEntryActivity was last
              // run then databaseFileToDecrypt won't be set so set it here.
              EnterMasterPassword.databaseFileToDecrypt = Utilities.getDatabaseFile(this);
  
              setResult(RESULT_ENTER_PW);
              finish();
          } else {
              if (Utilities.getSyncMethod(this).equals(Prefs.SyncMethod.DISABLED)) {
                  menu.findItem(R.id.sync).setEnabled(false);
              } else {
                  menu.findItem(R.id.sync).setEnabled(true);
              }
          }
          return true;
      }
  
      @Override
      public boolean onKeyDown(int keyCode, KeyEvent event) {
          if (keyCode == KeyEvent.KEYCODE_BACK) {
              new AlertDialog.Builder(this)
              .setIcon(android.R.drawable.ic_dialog_alert)
              .setTitle(R.string.confirm_exit_title)
              .setMessage(R.string.confirm_exit_message)
              .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
                  @Override
                  public void onClick(DialogInterface dialog, int which) {
                      FullAccountList.this.setResult(RESULT_EXIT);
                      FullAccountList.this.finish();
                  }
              })
              .setNegativeButton(R.string.no, null)
              .show();
              return true;
          } else {
              return super.onKeyDown(keyCode, event);
          }
      }
  
      @Override
      public boolean onOptionsItemSelected(MenuItem item) {
          boolean optionConsumed = false;
  
          switch (item.getItemId()) {
              case R.id.search:
                  onSearchRequested();
                  optionConsumed = true;
                  break;
              case R.id.add:
                  if (Utilities.isSyncRequired(this)) {
                      UIUtilities.showToast(this, R.string.sync_required);
                  } else {
                      Intent i = new Intent(FullAccountList.this, AddEditAccount.class);
                      i.putExtra(AddEditAccount.MODE, AddEditAccount.ADD_MODE);
                      startActivityForResult(i, AddEditAccount.EDIT_ACCOUNT_REQUEST_CODE);
                  }
                  break;
              case R.id.change_master_password:
                  if (Utilities.isSyncRequired(this)) {
                      UIUtilities.showToast(this, R.string.sync_required);
                  } else {
                      startActivity(new Intent(FullAccountList.this, ChangeMasterPassword.class));
                  }
                  break;
              case R.id.restore:
                  // Check to ensure there's a file to restore
                  File restoreFile = Utilities.getBackupFile(FullAccountList.this);
                  if (restoreFile.exists()) {
                      showDialog(CONFIRM_RESTORE_DIALOG);
                  } else {
                      String messageRes = getString(R.string.restore_file_doesnt_exist);
                      String message = String.format(messageRes, restoreFile.getAbsolutePath());
                      Toast.makeText(this, message, Toast.LENGTH_LONG).show();
                  }
                  break;
              case R.id.backup:
                  // If there's already a backup file prompt the user if they want to overwrite
                  File backupFile = Utilities.getBackupFile(FullAccountList.this);
                  if (backupFile.exists()) {
                      showDialog(CONFIRM_OVERWRITE_BACKUP_FILE);
                  } else {
                      backupDatabase();
                  }
                  break;
              case R.id.about:
                  showDialog(DIALOG_ABOUT);
                  break;
              case R.id.sync:
                  if (Utilities.getSyncMethod(this).equals(Prefs.SyncMethod.HTTP)) {
                      Intent i = new Intent(FullAccountList.this, SyncDatabaseViaHttpActivity.class);
                      startActivityForResult(i, SyncDatabaseActivity.SYNC_DB_REQUEST_CODE);
                  } else if (Utilities.getSyncMethod(this).equals(Prefs.SyncMethod.DROPBOX)) {
                      Intent i = new Intent(FullAccountList.this, SyncDatabaseViaDropboxActivity.class);
                      startActivityForResult(i, SyncDatabaseActivity.SYNC_DB_REQUEST_CODE);
                  }
                  break;
              case R.id.preferences:
                  startActivity(new Intent(this, Prefs.class));
                  break;
              case R.id.delete_db:
                  showDialog(CONFIRM_DELETE_DB_DIALOG);
                  break;
              case R.id.import_certificate:
                  showDialog(IMPORT_CERT_DIALOG);
                  break;
              case R.id.delete_certificate:
                  deleteCertificate();
                  break;
              case R.id.donate:
                  launchDonatePage();
                  break;
          }
  
          return optionConsumed;
      }
  
      @Override
      protected Dialog onCreateDialog(int id) {
          AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
  
          switch(id) {
          case CONFIRM_RESTORE_DIALOG:
              dialogBuilder.setMessage(getString(R.string.confirm_restore_overwrite))
                 .setCancelable(false)
                 .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                     public void onClick(DialogInterface dialog, int id) {
                         ((UPMApplication) getApplication()).restoreDatabase(FullAccountList.this);
                         // Clear the activity stack and bring up AppEntryActivity
                         // This is effectively restarting the application
                         Intent i = new Intent(FullAccountList.this, AppEntryActivity.class);
                         i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                         startActivity(i);
                         finish();
                     }
                 })
                 .setNegativeButton("No", new DialogInterface.OnClickListener() {
                     public void onClick(DialogInterface dialog, int id) {
                          dialog.cancel();
                     }
                 });
              break;
          case CONFIRM_OVERWRITE_BACKUP_FILE:
              File backupFile = Utilities.getBackupFile(FullAccountList.this);
              String messageRes = getString(R.string.backup_file_exists);
              String message = String.format(messageRes, backupFile.getAbsolutePath());
  
              dialogBuilder.setMessage(message)
                 .setCancelable(false)
                 .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                     public void onClick(DialogInterface dialog, int id) {
                         backupDatabase();
                     }
                 })
                 .setNegativeButton("No", new DialogInterface.OnClickListener() {
                     public void onClick(DialogInterface dialog, int id) {
                          dialog.cancel();
                     }
                 });
              break;
          case DIALOG_ABOUT:
              PackageInfo pinfo;
              String versionName = "<unknown>";
              int versionCode = 0;
              try {
                  pinfo = getPackageManager().getPackageInfo(getPackageName(), 0);
                  versionName = pinfo.versionName;
                  versionCode = pinfo.versionCode;
              } catch (NameNotFoundException e) {
                  Log.e("FullAccountList", e.getMessage(), e);
              }
  
              View v = LayoutInflater.from(this).inflate(R.layout.dialog, null);
              TextView text = (TextView) v.findViewById(R.id.dialogText);
              text.setText(getString(R.string.aboutText, versionName, versionCode));
  
              dialogBuilder
                  .setTitle(R.string.about)
                  .setIcon(android.R.drawable.ic_menu_info_details)
                  .setPositiveButton(R.string.homepage, new OnClickListener() {
                      public void onClick(DialogInterface dialog, int which) {
                          Uri uri = Uri.parse(getString(R.string.homepageUrl));
                          startActivity(new Intent(Intent.ACTION_VIEW, uri));
                      }
                  })
                  .setNeutralButton(R.string.donate, new OnClickListener() {
                      public void onClick(DialogInterface arg0, int arg1) {
                          launchDonatePage();
                      }
                  })
                  .setNegativeButton(R.string.close, null)
                  .setView(v);
              break;
          case CONFIRM_DELETE_DB_DIALOG:
              dialogBuilder.setMessage(getString(R.string.confirm_delete_db))
              .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                  public void onClick(DialogInterface dialog, int id) {
                      deleteDatabase();
                      // Clear the activity stack and bring up AppEntryActivity
                      // This is effectively restarting the application
                      Intent i = new Intent(FullAccountList.this, AppEntryActivity.class);
                      i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                      startActivity(i);
                      finish();
                  }
              })
              .setNegativeButton("No", new DialogInterface.OnClickListener() {
                  public void onClick(DialogInterface dialog, int id) {
                       dialog.cancel();
                  }
              });
              break;
          case IMPORT_CERT_DIALOG:
              String importCertMessageRes = getString(R.string.import_cert_message);
              String importCertMessage = String.format(importCertMessageRes, CERT_FILE_NAME);
  
              dialogBuilder
                  .setCancelable(false)
                  .setTitle(R.string.import_cert)
                  .setMessage(importCertMessage)
                  .setPositiveButton(R.string.import_cert, new DialogInterface.OnClickListener() {
                      public void onClick(DialogInterface dialog, int id) {
                          importCert();
                      }
                  })
                  .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                      public void onClick(DialogInterface dialog, int id) {
                          dialog.cancel();
                      }
                  });
              break;
          }
  
          return dialogBuilder.create();
      }
  
      private void deleteDatabase() {
          Utilities.getDatabaseFile(this).delete();
          Utilities.setDatabaseFileName(null, this);
      }
  
      private void backupDatabase() {
          File backupFile = Utilities.getBackupFile(FullAccountList.this);
          File databaseFile = Utilities.getDatabaseFile(this);
          if (((UPMApplication) getApplication()).copyFile(databaseFile, backupFile, this)) {
              String message = String.format(getString(R.string.backup_complete), backupFile.getAbsolutePath());
              UIUtilities.showToast(this, message, false);
          }
      }
  
      private void launchDonatePage() {
          Uri uri = Uri.parse(getString(R.string.donateURL));
          startActivity(new Intent(Intent.ACTION_VIEW, uri));
      }
  
      private void deleteCertificate() {
          File privateCertFile = getFileStreamPath(CERT_FILE_NAME);
          if (privateCertFile.exists()) {
              privateCertFile.delete();
              UIUtilities.showToast(this, R.string.cert_deleted, false);
          } else {
              UIUtilities.showToast(this, R.string.no_cert_available, false);
          }
      }
  
      /*
       * Import a Certifcate at a known location into the applications private files area.
       * This file will be used in the HTTPTransport.
       */
      private void importCert() {
          File certFile = new File(Environment.getExternalStorageDirectory(), CERT_FILE_NAME);
          File privateCertFile = getFileStreamPath(CERT_FILE_NAME);
  
          try {
              copyFile(certFile, privateCertFile);
              UIUtilities.showToast(this, R.string.cert_imported, false);
          } catch (IOException e) {
              Log.e(this.getClass().getName(), e.toString(), e);
              Toast.makeText(this, e.toString(), Toast.LENGTH_LONG).show();
          }
      }
  
      private void copyFile(File source, File dest) throws IOException {
          FileChannel sourceChannel = null;
          FileChannel destinationChannel = null;
          FileInputStream is = null;
          FileOutputStream os = null;
          try {
              is = new FileInputStream(source);
              sourceChannel = is.getChannel();
  
              File destFile = null;
              if (dest.isDirectory()) {
                  destFile = new File(dest, source.getName());
              } else {
                  destFile = dest;
              }
  
              os = new FileOutputStream(destFile);
              destinationChannel = os.getChannel();
              destinationChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
          } finally {
              if (sourceChannel != null) {
                  sourceChannel.close();
              }
              if (is != null) {
                  is.close();
              }
              if (destinationChannel != null) {
                  destinationChannel.close();
              }
              if (os != null) {
                  os.close();
              }
          }
      }
  
  }
  