/* * Copyright 2009-2016 Brian Pellin. * * This file is part of KeePassDroid. * * KeePassDroid 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. * * KeePassDroid 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 KeePassDroid. If not, see <http://www.gnu.org/licenses/>. * */ package com.keepassdroid; import android.annotation.TargetApi; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.ClipData; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; import android.text.InputType; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.EditText; import android.widget.ImageButton; import android.widget.TextView; import android.widget.Toast; import com.android.keepass.KeePass; import com.android.keepass.R; import com.keepassdroid.app.App; import com.keepassdroid.compat.BackupManagerCompat; import com.keepassdroid.compat.ClipDataCompat; import com.keepassdroid.compat.EditorCompat; import com.keepassdroid.compat.StorageAF; import com.keepassdroid.database.edit.LoadDB; import com.keepassdroid.database.edit.OnFinish; import com.keepassdroid.dialog.PasswordEncodingDialogHelper; import com.keepassdroid.fileselect.BrowserDialog; import com.keepassdroid.intents.Intents; import com.keepassdroid.settings.AppSettingsActivity; import com.keepassdroid.utils.EmptyUtils; import com.keepassdroid.utils.Interaction; import com.keepassdroid.utils.UriUtil; import com.keepassdroid.utils.Util; import java.io.File; import java.io.FileNotFoundException; public class PasswordActivity extends LockingActivity { public static final String KEY_DEFAULT_FILENAME = "defaultFileName"; private static final String KEY_FILENAME = "fileName"; private static final String KEY_KEYFILE = "keyFile"; private static final String KEY_PASSWORD = "password"; private static final String KEY_LAUNCH_IMMEDIATELY = "launchImmediately"; private static final String VIEW_INTENT = "android.intent.action.VIEW"; private static final int FILE_BROWSE = 256; public static final int GET_CONTENT = 257; private static final int OPEN_DOC = 258; private Uri mDbUri = null; private Uri mKeyUri = null; private boolean mRememberKeyfile; SharedPreferences prefs; public static void Launch(Activity act, String fileName) throws FileNotFoundException { Launch(act,fileName,""); } public static void Launch(Activity act, String fileName, String keyFile) throws FileNotFoundException { if (EmptyUtils.isNullOrEmpty(fileName)) { throw new FileNotFoundException(); } Uri uri = UriUtil.parseDefaultFile(fileName); String scheme = uri.getScheme(); if (!EmptyUtils.isNullOrEmpty(scheme) && scheme.equalsIgnoreCase("file")) { File dbFile = new File(uri.getPath()); if (!dbFile.exists()) { throw new FileNotFoundException(); } } Intent i = new Intent(act, PasswordActivity.class); i.putExtra(KEY_FILENAME, fileName); i.putExtra(KEY_KEYFILE, keyFile); act.startActivityForResult(i, 0); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case KeePass.EXIT_NORMAL: setEditText(R.id.password, ""); App.getDB().clear(); break; case KeePass.EXIT_LOCK: setResult(KeePass.EXIT_LOCK); setEditText(R.id.password, ""); finish(); App.getDB().clear(); break; case FILE_BROWSE: if (resultCode == RESULT_OK) { String filename = data.getDataString(); if (filename != null) { EditText fn = (EditText) findViewById(R.id.pass_keyfile); fn.setText(filename); mKeyUri = UriUtil.parseDefaultFile(filename); } } break; case GET_CONTENT: case OPEN_DOC: if (resultCode == RESULT_OK) { if (data != null) { Uri uri = data.getData(); if (uri != null) { if (requestCode==GET_CONTENT) { uri = UriUtil.translate(this, uri); } String path = uri.toString(); if (path != null) { EditText fn = (EditText) findViewById(R.id.pass_keyfile); fn.setText(path); } mKeyUri = uri; } } } break; } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent i = getIntent(); prefs = PreferenceManager.getDefaultSharedPreferences(this); mRememberKeyfile = prefs.getBoolean(getString(R.string.keyfile_key), getResources().getBoolean(R.bool.keyfile_default)); setContentView(R.layout.password); new InitTask().execute(i); } @Override protected void onResume() { super.onResume(); // If the application was shutdown make sure to clear the password field, if it // was saved in the instance state if (App.isShutdown()) { TextView password = (TextView) findViewById(R.id.password); password.setText(""); } // Clear the shutdown flag App.clearShutdown(); } private void retrieveSettings() { String defaultFilename = prefs.getString(KEY_DEFAULT_FILENAME, ""); if (!EmptyUtils.isNullOrEmpty(mDbUri.getPath()) && UriUtil.equalsDefaultfile(mDbUri, defaultFilename)) { CheckBox checkbox = (CheckBox) findViewById(R.id.default_database); checkbox.setChecked(true); } } private Uri getKeyFile(Uri dbUri) { if ( mRememberKeyfile ) { return App.getFileHistory().getFileByName(dbUri); } else { return null; } } private void populateView() { String db = (mDbUri == null) ? "" : mDbUri.toString(); setEditText(R.id.filename, db); String key = (mKeyUri == null) ? "" : mKeyUri.toString(); setEditText(R.id.pass_keyfile, key); } /* private void errorMessage(CharSequence text) { Toast.makeText(this, text, Toast.LENGTH_LONG).show(); } */ private void errorMessage(int resId) { Toast.makeText(this, resId, Toast.LENGTH_LONG).show(); } private class DefaultCheckChange implements CompoundButton.OnCheckedChangeListener { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { String newDefaultFileName; if (isChecked) { newDefaultFileName = mDbUri.toString(); } else { newDefaultFileName = ""; } SharedPreferences.Editor editor = prefs.edit(); editor.putString(KEY_DEFAULT_FILENAME, newDefaultFileName); EditorCompat.apply(editor); BackupManagerCompat backupManager = new BackupManagerCompat(PasswordActivity.this); backupManager.dataChanged(); } } private class OkClickHandler implements View.OnClickListener { public void onClick(View view) { String pass = getEditText(R.id.password); String key = getEditText(R.id.pass_keyfile); loadDatabase(pass, key); } } private void loadDatabase(String pass, String keyfile) { loadDatabase(pass, UriUtil.parseDefaultFile(keyfile)); } private void loadDatabase(String pass, Uri keyfile) { if ( pass.length() == 0 && (keyfile == null || keyfile.toString().length() == 0)) { errorMessage(R.string.error_nopass); return; } // Clear before we load Database db = App.getDB(); db.clear(); // Clear the shutdown flag App.clearShutdown(); Handler handler = new Handler(); LoadDB task = new LoadDB(db, PasswordActivity.this, mDbUri, pass, keyfile, new AfterLoad(handler, db)); ProgressTask pt = new ProgressTask(PasswordActivity.this, task, R.string.loading_database); pt.run(); } private String getEditText(int resId) { return Util.getEditText(this, resId); } private void setEditText(int resId, String str) { TextView te = (TextView) findViewById(resId); assert(te == null); if (te != null) { te.setText(str); } } @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); MenuInflater inflate = getMenuInflater(); inflate.inflate(R.menu.password, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch ( item.getItemId() ) { case R.id.menu_about: AboutDialog dialog = new AboutDialog(this); dialog.show(); return true; case R.id.menu_app_settings: AppSettingsActivity.Launch(this); return true; } return super.onOptionsItemSelected(item); } private final class AfterLoad extends OnFinish { private Database db; public AfterLoad(Handler handler, Database db) { super(handler); this.db = db; } @Override public void run() { if ( db.passwordEncodingError) { PasswordEncodingDialogHelper dialog = new PasswordEncodingDialogHelper(); dialog.show(PasswordActivity.this, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { GroupActivity.Launch(PasswordActivity.this); } }); } else if ( mSuccess ) { GroupActivity.Launch(PasswordActivity.this); } else { displayMessage(PasswordActivity.this); } } } private class InitTask extends AsyncTask<Intent, Void, Integer> { String password = ""; boolean launch_immediately = false; @Override protected Integer doInBackground(Intent... args) { Intent i = args[0]; String action = i.getAction();; if ( action != null && action.equals(VIEW_INTENT) ) { Uri incoming = i.getData(); mDbUri = incoming; mKeyUri = ClipDataCompat.getUriFromIntent(i, KEY_KEYFILE); if (incoming == null) { return R.string.error_can_not_handle_uri; } else if (incoming.getScheme().equals("file")) { String fileName = incoming.getPath(); if (fileName.length() == 0) { // No file name return R.string.FileNotFound; } File dbFile = new File(fileName); if (!dbFile.exists()) { // File does not exist return R.string.FileNotFound; } if(mKeyUri == null) mKeyUri = getKeyFile(mDbUri); } else if (incoming.getScheme().equals("content")) { if(mKeyUri == null) mKeyUri = getKeyFile(mDbUri); } else { return R.string.error_can_not_handle_uri; } password = i.getStringExtra(KEY_PASSWORD); launch_immediately = i.getBooleanExtra(KEY_LAUNCH_IMMEDIATELY, false); } else { mDbUri = UriUtil.parseDefaultFile(i.getStringExtra(KEY_FILENAME)); mKeyUri = UriUtil.parseDefaultFile(i.getStringExtra(KEY_KEYFILE)); password = i.getStringExtra(KEY_PASSWORD); launch_immediately = i.getBooleanExtra(KEY_LAUNCH_IMMEDIATELY, false); if ( mKeyUri == null || mKeyUri.toString().length() == 0) { mKeyUri = getKeyFile(mDbUri); } } return null; } public void onPostExecute(Integer result) { if(result != null) { Toast.makeText(PasswordActivity.this, result, Toast.LENGTH_LONG).show(); finish(); return; } populateView(); Button confirmButton = (Button) findViewById(R.id.pass_ok); confirmButton.setOnClickListener(new OkClickHandler()); CheckBox checkBox = (CheckBox) findViewById(R.id.show_password); // Show or hide password checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { TextView password = (TextView) findViewById(R.id.password); if ( isChecked ) { password.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); } else { password.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); } } }); if (password != null) { TextView tv_password = (TextView) findViewById(R.id.password); tv_password.setText(password); } CheckBox defaultCheck = (CheckBox) findViewById(R.id.default_database); defaultCheck.setOnCheckedChangeListener(new DefaultCheckChange()); ImageButton browse = (ImageButton) findViewById(R.id.browse_button); browse.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { if (StorageAF.useStorageFramework(PasswordActivity.this)) { Intent i = new Intent(StorageAF.ACTION_OPEN_DOCUMENT); i.addCategory(Intent.CATEGORY_OPENABLE); i.setType("*/*"); startActivityForResult(i, OPEN_DOC); } else { Intent i = new Intent(Intent.ACTION_GET_CONTENT); i.addCategory(Intent.CATEGORY_OPENABLE); i.setType("*/*"); try { startActivityForResult(i, GET_CONTENT); } catch (ActivityNotFoundException e) { lookForOpenIntentsFilePicker(); } } } private void lookForOpenIntentsFilePicker() { if (Interaction.isIntentAvailable(PasswordActivity.this, Intents.OPEN_INTENTS_FILE_BROWSE)) { Intent i = new Intent(Intents.OPEN_INTENTS_FILE_BROWSE); // Get file path parent if possible try { if (mDbUri != null && mDbUri.toString().length() > 0) { if (mDbUri.getScheme().equals("file")) { File keyfile = new File(mDbUri.getPath()); File parent = keyfile.getParentFile(); if (parent != null) { i.setData(Uri.parse("file://" + parent.getAbsolutePath())); } } } } catch (Exception e) { // Ignore } try { startActivityForResult(i, FILE_BROWSE); } catch (ActivityNotFoundException e) { showBrowserDialog(); } } else { showBrowserDialog(); } } private void showBrowserDialog() { BrowserDialog diag = new BrowserDialog(PasswordActivity.this); diag.show(); } }); retrieveSettings(); if (launch_immediately) loadDatabase(password, mKeyUri); } } }