diff --git a/ReadMe.md b/ReadMe.md
index 7baf0d9d6..c7d73a427 100644
--- a/ReadMe.md
+++ b/ReadMe.md
@@ -4,13 +4,14 @@
### Features
-- Create database file / entries and groups
+- Simplified creation of the database file
+- Create entries and groups
- Open database, copy username / password, open URI / URL
- Fingerprint for fast unlocking
- Material design with themes
- Device integration and AutoFill (In progress)
-
+
## What is KeePass?
diff --git a/app/build.gradle b/app/build.gradle
index b0f2970f1..88c9b80ae 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -63,8 +63,10 @@ dependencies {
compile "com.android.support:design:$supportVersion"
compile "com.android.support:preference-v7:$supportVersion"
compile "com.android.support:preference-v14:$supportVersion"
+ compile "com.android.support:cardview-v7:$supportVersion"
compile "com.madgag.spongycastle:core:$spongycastleVersion"
compile "com.madgag.spongycastle:prov:$spongycastleVersion"
compile "joda-time:joda-time:2.9.9"
compile "org.sufficientlysecure:html-textview:3.5"
+ compile "com.nononsenseapps:filepicker:4.1.0"
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 123ca35ef..19770da5d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,7 +1,8 @@
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.kunzisoft.keepass"
+ android:installLocation="auto">
+ android:theme="@style/KeepassDXStyle.Light"
+ tools:replace="android:theme">
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/com/keepassdroid/AssignMasterKeyDialog.java b/app/src/main/java/com/keepassdroid/AssignMasterKeyDialog.java
new file mode 100644
index 000000000..ff2dcaf3f
--- /dev/null
+++ b/app/src/main/java/com/keepassdroid/AssignMasterKeyDialog.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.keepassdroid;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AlertDialog;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CompoundButton;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.keepassdroid.utils.EmptyUtils;
+import com.keepassdroid.utils.UriUtil;
+import com.keepassdroid.view.KeyFileHelper;
+import com.kunzisoft.keepass.R;
+
+public class AssignMasterKeyDialog extends DialogFragment {
+
+ private String masterPassword;
+ private Uri mKeyfile;
+
+ private View rootView;
+ private CompoundButton passwordCheckBox;
+ private TextView passView;
+ private TextView passConfView;
+ private CompoundButton keyfileCheckBox;
+ private TextView keyfileView;
+
+ private AssignPasswordDialogListener mListener;
+
+ private KeyFileHelper keyFileHelper;
+
+ public interface AssignPasswordDialogListener {
+ void onAssignKeyDialogPositiveClick(boolean masterPasswordChecked, String masterPassword,
+ boolean keyFileChecked, Uri keyFile);
+ void onAssignKeyDialogNegativeClick(boolean masterPasswordChecked, String masterPassword,
+ boolean keyFileChecked, Uri keyFile);
+ }
+
+ @Override
+ public void onAttach(Context activity) {
+ super.onAttach(activity);
+ try {
+ mListener = (AssignPasswordDialogListener) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString()
+ + " must implement " + AssignPasswordDialogListener.class.getName());
+ }
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+
+ rootView = inflater.inflate(R.layout.set_password, null);
+ builder.setView(rootView)
+ .setTitle(R.string.assign_master_key)
+ // Add action buttons
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ }
+ });
+
+ passwordCheckBox = (CompoundButton) rootView.findViewById(R.id.password_checkbox);
+ passView = (TextView) rootView.findViewById(R.id.pass_password);
+ passView.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ passwordCheckBox.setChecked(true);
+ }
+ });
+ passConfView = (TextView) rootView.findViewById(R.id.pass_conf_password);
+
+ keyfileCheckBox = (CompoundButton) rootView.findViewById(R.id.keyfile_checkox);
+ keyfileView = (TextView) rootView.findViewById(R.id.pass_keyfile);
+ keyfileView.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ keyfileCheckBox.setChecked(true);
+ }
+ });
+
+ keyFileHelper = new KeyFileHelper(this);
+ rootView.findViewById(R.id.browse_button)
+ .setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ keyFileHelper.getOpenFileOnClickViewListener().onClick(view);
+ }
+ });
+
+ AlertDialog dialog = builder.create();
+
+ dialog.setOnShowListener(new DialogInterface.OnShowListener() {
+ @Override
+ public void onShow(final DialogInterface dialog) {
+ Button positiveButton = ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE);
+ positiveButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+
+ masterPassword = "";
+ mKeyfile = null;
+
+ boolean error = verifyPassword() || verifyFile();
+
+ if (!passwordCheckBox.isChecked() && !keyfileCheckBox.isChecked()) {
+ error = true;
+ showNoKeyConfirmationDialog();
+ }
+
+ if (!error) {
+ mListener.onAssignKeyDialogPositiveClick(
+ passwordCheckBox.isChecked(), masterPassword,
+ keyfileCheckBox.isChecked(), mKeyfile);
+ dismiss();
+ }
+ }
+ });
+ Button negativeButton = ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_NEGATIVE);
+ negativeButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ mListener.onAssignKeyDialogNegativeClick(
+ passwordCheckBox.isChecked(), masterPassword,
+ keyfileCheckBox.isChecked(), mKeyfile);
+ dismiss();
+ }
+ });
+ }
+ });
+
+
+ return dialog;
+ }
+
+ private boolean verifyPassword() {
+ boolean error = false;
+ if (passwordCheckBox.isChecked()) {
+ masterPassword = passView.getText().toString();
+ String confpass = passConfView.getText().toString();
+
+ // Verify that passwords match
+ if (!masterPassword.equals(confpass)) {
+ error = true;
+ // Passwords do not match
+ Toast.makeText(getContext(), R.string.error_pass_match, Toast.LENGTH_LONG).show();
+ }
+
+ if (masterPassword == null || masterPassword.isEmpty()) {
+ error = true;
+ showEmptyPasswordConfirmationDialog();
+ }
+ }
+ return error;
+ }
+
+ private boolean verifyFile() {
+ boolean error = false;
+ if (keyfileCheckBox.isChecked()) {
+ Uri keyfile = UriUtil.parseDefaultFile(keyfileView.getText().toString());
+ mKeyfile = keyfile;
+
+ // Verify that a keyfile is set
+ if (EmptyUtils.isNullOrEmpty(keyfile)) {
+ error = true;
+ Toast.makeText(getContext(), R.string.error_nokeyfile, Toast.LENGTH_LONG).show();
+ }
+ }
+ return error;
+ }
+
+ private void showEmptyPasswordConfirmationDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setMessage(R.string.warning_empty_password)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ if (!verifyFile()) {
+ mListener.onAssignKeyDialogPositiveClick(
+ passwordCheckBox.isChecked(), masterPassword,
+ keyfileCheckBox.isChecked(), mKeyfile);
+ AssignMasterKeyDialog.this.dismiss();
+ }
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {}
+ });
+ builder.create().show();
+ }
+
+ private void showNoKeyConfirmationDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setMessage(R.string.warning_no_encryption_key)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ mListener.onAssignKeyDialogPositiveClick(
+ passwordCheckBox.isChecked(), masterPassword,
+ keyfileCheckBox.isChecked(), mKeyfile);
+ AssignMasterKeyDialog.this.dismiss();
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {}
+ });
+ builder.create().show();
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ keyFileHelper.onActivityResultCallback(requestCode, resultCode, data,
+ new KeyFileHelper.KeyFileCallback() {
+ @Override
+ public void onKeyFileResultCallback(Uri uri) {
+ if(uri != null) {
+ Uri pathString = UriUtil.parseDefaultFile(uri.toString());
+ if (pathString != null) {
+ keyfileCheckBox.setChecked(true);
+ keyfileView.setText(pathString.toString());
+ }
+ }
+ }
+ });
+ }
+}
diff --git a/app/src/main/java/com/keepassdroid/CancelDialog.java b/app/src/main/java/com/keepassdroid/CancelDialog.java
deleted file mode 100644
index 322270d61..000000000
--- a/app/src/main/java/com/keepassdroid/CancelDialog.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.keepassdroid;
-
-import android.app.Dialog;
-import android.content.Context;
-
-public class CancelDialog extends Dialog {
-
- private boolean mCanceled = false;
-
- public CancelDialog(Context context) {
- super(context);
- }
-
- public boolean canceled() {
- return mCanceled;
- }
-
- @Override
- public void cancel() {
- super.cancel();
- mCanceled = true;
- }
-}
diff --git a/app/src/main/java/com/keepassdroid/CreateFileDialog.java b/app/src/main/java/com/keepassdroid/CreateFileDialog.java
new file mode 100644
index 000000000..0f941d11a
--- /dev/null
+++ b/app/src/main/java/com/keepassdroid/CreateFileDialog.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2017 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.keepassdroid;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AlertDialog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Spinner;
+
+import com.keepassdroid.utils.UriUtil;
+import com.kunzisoft.keepass.R;
+import com.nononsenseapps.filepicker.FilePickerActivity;
+import com.nononsenseapps.filepicker.Utils;
+
+import java.io.File;
+
+public class CreateFileDialog extends DialogFragment implements AdapterView.OnItemSelectedListener{
+
+ private final int FILE_CODE = 3853;
+
+ private EditText folderPathView;
+ private EditText fileNameView;
+ private DefinePathDialogListener mListener;
+ private String extension;
+
+ private Uri uriPath;
+
+ public interface DefinePathDialogListener {
+ boolean onDefinePathDialogPositiveClick(Uri pathFile);
+ boolean onDefinePathDialogNegativeClick(Uri pathFile);
+ }
+
+ @Override
+ public void onAttach(Context activity) {
+ super.onAttach(activity);
+ try {
+ mListener = (DefinePathDialogListener) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString()
+ + " must implement " + DefinePathDialogListener.class.getName());
+ }
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+
+ View rootView = inflater.inflate(R.layout.file_creation, null);
+ builder.setView(rootView)
+ .setTitle(R.string.create_keepass_file)
+ // Add action buttons
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {}
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {}
+ });
+
+ // Folder selection
+ View browseView = rootView.findViewById(R.id.browse_button);
+ folderPathView = (EditText) rootView.findViewById(R.id.folder_path);
+ fileNameView = (EditText) rootView.findViewById(R.id.filename);
+ String defaultPath = Environment.getExternalStorageDirectory().getPath()
+ + getString(R.string.database_file_path_default);
+ folderPathView.setText(defaultPath);
+ browseView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent i = new Intent(getContext(), FilePickerStylishActivity.class);
+ i.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false);
+ i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, true);
+ i.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR);
+ i.putExtra(FilePickerActivity.EXTRA_START_PATH,
+ Environment.getExternalStorageDirectory().getPath());
+ startActivityForResult(i, FILE_CODE);
+ }
+ });
+
+ // Init path
+ uriPath = null;
+
+ // Extension
+ extension = getString(R.string.database_file_extension_default);
+ Spinner spinner = (Spinner) rootView.findViewById(R.id.file_types);
+ spinner.setOnItemSelectedListener(this);
+
+ // Spinner Drop down elements
+ String[] fileTypes = getResources().getStringArray(R.array.file_types);
+ ArrayAdapter dataAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_item, fileTypes);
+ dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spinner.setAdapter(dataAdapter);
+
+ AlertDialog dialog = builder.create();
+
+ dialog.setOnShowListener(new DialogInterface.OnShowListener() {
+ @Override
+ public void onShow(final DialogInterface dialog) {
+ Button positiveButton = ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE);
+ positiveButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ if(mListener.onDefinePathDialogPositiveClick(buildPath()))
+ CreateFileDialog.this.dismiss();
+ }
+ });
+ Button negativeButton = ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_NEGATIVE);
+ negativeButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ if(mListener.onDefinePathDialogNegativeClick(buildPath()))
+ CreateFileDialog.this.dismiss();
+ }
+ });
+ }
+ });
+
+ return dialog;
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == FILE_CODE && resultCode == Activity.RESULT_OK) {
+ uriPath = data.getData();
+ if (uriPath != null) {
+ File file = Utils.getFileForUri(uriPath);
+ folderPathView.setText(file.getPath());
+ }
+ }
+ }
+
+ @Override
+ public void onItemSelected(AdapterView> adapterView, View view, int position, long id) {
+ extension = adapterView.getItemAtPosition(position).toString();
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> adapterView) {
+ // Do nothing
+ }
+
+ private Uri buildPath() {
+ Uri path = new Uri.Builder().path(folderPathView.getText().toString())
+ .appendPath(fileNameView.getText().toString() + extension)
+ .build();
+ path = UriUtil.translate(getContext(), path);
+ return path;
+ }
+}
diff --git a/app/src/main/java/com/keepassdroid/FilePickerStylishActivity.java b/app/src/main/java/com/keepassdroid/FilePickerStylishActivity.java
new file mode 100644
index 000000000..61952740e
--- /dev/null
+++ b/app/src/main/java/com/keepassdroid/FilePickerStylishActivity.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.keepassdroid;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.annotation.StyleRes;
+import android.util.Log;
+
+import com.keepassdroid.stylish.Stylish;
+import com.kunzisoft.keepass.R;
+import com.nononsenseapps.filepicker.FilePickerActivity;
+
+
+public class FilePickerStylishActivity extends FilePickerActivity {
+
+ private @StyleRes
+ int themeId;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ this.themeId = FilePickerStylish.getThemeId(this);
+ setTheme(themeId);
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if(FilePickerStylish.getThemeId(this) != this.themeId) {
+ Log.d(this.getClass().getName(), "Theme change detected, restarting activity");
+ this.recreate();
+ }
+ }
+
+ public static class FilePickerStylish extends Stylish {
+ public static @StyleRes int getThemeId(Context context) {
+ if (themeString.equals(context.getString(R.string.list_style_name_night)))
+ return R.style.KeepassDXStyle_FilePickerStyle_Night;
+
+ return R.style.KeepassDXStyle_FilePickerStyle_Light;
+ }
+ }
+}
diff --git a/app/src/main/java/com/keepassdroid/GroupBaseActivity.java b/app/src/main/java/com/keepassdroid/GroupBaseActivity.java
index e5cc26ead..b0b633b6c 100644
--- a/app/src/main/java/com/keepassdroid/GroupBaseActivity.java
+++ b/app/src/main/java/com/keepassdroid/GroupBaseActivity.java
@@ -25,6 +25,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
@@ -40,8 +41,6 @@ import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
-import com.kunzisoft.keepass.KeePass;
-import com.kunzisoft.keepass.R;
import com.keepassdroid.app.App;
import com.keepassdroid.compat.ActivityCompat;
import com.keepassdroid.compat.EditorCompat;
@@ -49,10 +48,14 @@ import com.keepassdroid.database.PwGroup;
import com.keepassdroid.database.edit.OnFinish;
import com.keepassdroid.search.SearchResultsActivity;
import com.keepassdroid.utils.MenuUtil;
+import com.keepassdroid.view.AssignPasswordHelper;
import com.keepassdroid.view.ClickView;
import com.keepassdroid.view.GroupViewOnlyView;
+import com.kunzisoft.keepass.KeePass;
+import com.kunzisoft.keepass.R;
-public abstract class GroupBaseActivity extends LockCloseListActivity {
+public abstract class GroupBaseActivity extends LockCloseListActivity
+ implements AssignMasterKeyDialog.AssignPasswordDialogListener {
protected ListView mList;
protected ListAdapter mAdapter;
@@ -265,8 +268,26 @@ public abstract class GroupBaseActivity extends LockCloseListActivity {
}
+ @Override
+ public void onAssignKeyDialogPositiveClick(
+ boolean masterPasswordChecked, String masterPassword,
+ boolean keyFileChecked, Uri keyFile) {
+
+ AssignPasswordHelper assignPasswordHelper =
+ new AssignPasswordHelper(this,
+ masterPassword, keyFile);
+ assignPasswordHelper.assignPasswordInDatabase(null);
+ }
+
+ @Override
+ public void onAssignKeyDialogNegativeClick(
+ boolean masterPasswordChecked, String masterPassword,
+ boolean keyFileChecked, Uri keyFile) {
+
+ }
+
private void setPassword() {
- SetPasswordDialog dialog = new SetPasswordDialog();
+ AssignMasterKeyDialog dialog = new AssignMasterKeyDialog();
dialog.show(getSupportFragmentManager(), "passwordDialog");
}
diff --git a/app/src/main/java/com/keepassdroid/PasswordActivity.java b/app/src/main/java/com/keepassdroid/PasswordActivity.java
index bb4a2e0c5..b318505fb 100644
--- a/app/src/main/java/com/keepassdroid/PasswordActivity.java
+++ b/app/src/main/java/com/keepassdroid/PasswordActivity.java
@@ -20,7 +20,6 @@
package com.keepassdroid;
import android.app.Activity;
-import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
@@ -52,21 +51,18 @@ 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.fingerprint.FingerPrintAnimatedVector;
import com.keepassdroid.fingerprint.FingerPrintHelper;
-import com.keepassdroid.intents.Intents;
import com.keepassdroid.settings.PrefsUtil;
import com.keepassdroid.utils.EmptyUtils;
-import com.keepassdroid.utils.Interaction;
import com.keepassdroid.utils.MenuUtil;
import com.keepassdroid.utils.UriUtil;
import com.keepassdroid.utils.Util;
import com.keepassdroid.view.FingerPrintDialog;
+import com.keepassdroid.view.KeyFileHelper;
import com.kunzisoft.keepass.KeePass;
import com.kunzisoft.keepass.R;
@@ -84,10 +80,6 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
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;
@@ -100,12 +92,19 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
private int mode;
private static final String PREF_KEY_VALUE_PREFIX = "valueFor_"; // key is a combination of db file name and this prefix
private static final String PREF_KEY_IV_PREFIX = "ivFor_"; // key is a combination of db file name and this prefix
+
private View fingerprintContainerView;
private View fingerprintImageView;
private FingerPrintAnimatedVector fingerPrintAnimatedVector;
private TextView fingerprintTextView;
+ private TextView filenameView;
private EditText passwordView;
- private Button confirmButton;
+ private EditText keyFileView;
+ private Button confirmButtonView;
+ private CheckBox checkboxPasswordView;
+ private CheckBox checkboxKeyfileView;
+
+ private KeyFileHelper keyFileHelper;
public static void Launch(
Activity act,
@@ -122,6 +121,7 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
}
Uri uri = UriUtil.parseDefaultFile(fileName);
+ assert uri != null;
String scheme = uri.getScheme();
if (!EmptyUtils.isNullOrEmpty(scheme) && scheme.equalsIgnoreCase("file")) {
@@ -146,49 +146,30 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
Intent data) {
super.onActivityResult(requestCode, resultCode, data);
+ keyFileHelper.onActivityResultCallback(requestCode, resultCode, data,
+ new KeyFileHelper.KeyFileCallback() {
+ @Override
+ public void onKeyFileResultCallback(Uri uri) {
+ if(uri != null) {
+ keyFileView.setText(uri.toString());
+ }
+ }
+ });
+
switch (requestCode) {
case KeePass.EXIT_NORMAL:
- setEditText(R.id.password, "");
+ setEmptyViews();
App.getDB().clear();
break;
case KeePass.EXIT_LOCK:
setResult(KeePass.EXIT_LOCK);
- setEditText(R.id.password, "");
+ setEmptyViews();
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
@@ -210,11 +191,40 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
- confirmButton = (Button) findViewById(R.id.pass_ok);
+ confirmButtonView = (Button) findViewById(R.id.pass_ok);
fingerprintContainerView = findViewById(R.id.fingerprint_container);
fingerprintImageView = findViewById(R.id.fingerprint_image);
fingerprintTextView = (TextView) findViewById(R.id.fingerprint_label);
+ filenameView = (TextView) findViewById(R.id.filename);
passwordView = (EditText) findViewById(R.id.password);
+ keyFileView = (EditText) findViewById(R.id.pass_keyfile);
+ checkboxPasswordView = (CheckBox) findViewById(R.id.password_checkbox);
+ checkboxKeyfileView = (CheckBox) findViewById(R.id.keyfile_checkox);
+
+ passwordView.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ checkboxPasswordView.setChecked(true);
+ }
+ });
+ keyFileView.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ checkboxKeyfileView.setChecked(true);
+ }
+ });
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
fingerPrintAnimatedVector = new FingerPrintAnimatedVector(this,
@@ -231,8 +241,7 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
// 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("");
+ setEmptyViews();
}
// Clear the shutdown flag
@@ -248,10 +257,17 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
}
}
+ private void setEmptyViews() {
+ passwordView.setText("");
+ keyFileView.setText("");
+ checkboxPasswordView.setChecked(false);
+ checkboxKeyfileView.setChecked(false);
+ }
+
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);
+ CompoundButton checkbox = (CompoundButton) findViewById(R.id.default_database);
checkbox.setChecked(true);
}
}
@@ -266,14 +282,12 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
private void populateView() {
String db = (mDbUri == null) ? "" : mDbUri.toString();
- setEditText(R.id.filename, db);
+ if (!db.isEmpty())
+ filenameView.setText(db);
String key = (mKeyUri == null) ? "" : mKeyUri.toString();
- setEditText(R.id.pass_keyfile, key);
- }
-
- private void errorMessage(int resId) {
- Toast.makeText(this, resId, Toast.LENGTH_LONG).show();
+ if (!key.isEmpty())
+ keyFileView.setText(key);
}
// fingerprint related code here
@@ -456,7 +470,7 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
.putString(getPreferenceKeyIvSpec(), ivSpec)
.apply();
// and remove visual input to reset UI
- confirmButton.performClick();
+ confirmButtonView.performClick();
fingerprintTextView.setText(R.string.encrypted_value_stored);
}
@@ -464,7 +478,7 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
public void handleDecryptedResult(final String value) {
// on decrypt enter it for the purchase/login action
passwordView.setText(value);
- confirmButton.performClick();
+ confirmButtonView.performClick();
}
@RequiresApi(api = Build.VERSION_CODES.M)
@@ -511,8 +525,8 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
private class OkClickHandler implements View.OnClickListener {
public void onClick(View view) {
- String pass = getEditText(R.id.password);
- String key = getEditText(R.id.pass_keyfile);
+ String pass = passwordView.getText().toString();
+ String key = keyFileView.getText().toString();
loadDatabase(pass, key);
}
}
@@ -526,10 +540,6 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
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();
@@ -538,6 +548,13 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
// Clear the shutdown flag
App.clearShutdown();
+ if (!checkboxPasswordView.isChecked()) {
+ pass = "";
+ }
+ if (!checkboxKeyfileView.isChecked()) {
+ keyfile = null;
+ }
+
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);
@@ -548,15 +565,6 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
return Util.getEditText(this, resId);
}
- private void setEditText(
- int resId,
- String str) {
- TextView te = (TextView) findViewById(resId);
- if (te != null) {
- te.setText(str);
- }
- }
-
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
@@ -583,7 +591,7 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
private Database db;
- public AfterLoad(
+ AfterLoad(
Handler handler,
Database db) {
super(handler);
@@ -683,69 +691,15 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
confirmButton.setOnClickListener(new OkClickHandler());
if (password != null) {
- TextView tv_password = (TextView) findViewById(R.id.password);
- tv_password.setText(password);
+ passwordView.setText(password);
}
- CheckBox defaultCheck = (CheckBox) findViewById(R.id.default_database);
+ CompoundButton defaultCheck = (CompoundButton) findViewById(R.id.default_database);
defaultCheck.setOnCheckedChangeListener(new DefaultCheckChange());
- View browse = 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 browserDialog = new BrowserDialog(PasswordActivity.this);
- browserDialog.show();
- }
- });
+ View browseView = findViewById(R.id.browse_button);
+ keyFileHelper = new KeyFileHelper(PasswordActivity.this);
+ browseView.setOnClickListener(keyFileHelper.getOpenFileOnClickViewListener());
retrieveSettings();
diff --git a/app/src/main/java/com/keepassdroid/SetPasswordDialog.java b/app/src/main/java/com/keepassdroid/SetPasswordDialog.java
deleted file mode 100644
index 740a4bccf..000000000
--- a/app/src/main/java/com/keepassdroid/SetPasswordDialog.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.keepassdroid;
-
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.support.annotation.NonNull;
-import android.support.v4.app.DialogFragment;
-import android.support.v7.app.AlertDialog;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.kunzisoft.keepass.R;
-import com.keepassdroid.app.App;
-import com.keepassdroid.database.edit.FileOnFinish;
-import com.keepassdroid.database.edit.OnFinish;
-import com.keepassdroid.database.edit.SetPassword;
-import com.keepassdroid.utils.EmptyUtils;
-import com.keepassdroid.utils.UriUtil;
-
-public class SetPasswordDialog extends DialogFragment {
-
- private final static String FINISH_TAG = "FINISH_TAG";
-
- private byte[] masterKey;
- private Uri mKeyfile;
- private FileOnFinish mFinish;
- private View rootView;
-
- public byte[] getKey() {
- return masterKey;
- }
-
- public Uri keyfile() {
- return mKeyfile;
- }
-
- public static SetPasswordDialog newInstance(FileOnFinish finish) {
- SetPasswordDialog setPasswordDialog = new SetPasswordDialog();
-
- Bundle args = new Bundle();
- args.putSerializable(FINISH_TAG, finish);
- setPasswordDialog.setArguments(args);
-
- return setPasswordDialog;
- }
-
- @NonNull
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- LayoutInflater inflater = getActivity().getLayoutInflater();
-
- if(getArguments() != null && getArguments().containsKey(FINISH_TAG)) {
- mFinish = (FileOnFinish) getArguments().getSerializable(FINISH_TAG);
- }
-
- rootView = inflater.inflate(R.layout.set_password, null);
- builder.setView(rootView)
- // Add action buttons
- .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- TextView passView = (TextView) rootView.findViewById(R.id.pass_password);
- String pass = passView.getText().toString();
- TextView passConfView = (TextView) rootView.findViewById(R.id.pass_conf_password);
- String confpass = passConfView.getText().toString();
-
- // Verify that passwords match
- if ( ! pass.equals(confpass) ) {
- // Passwords do not match
- Toast.makeText(getContext(), R.string.error_pass_match, Toast.LENGTH_LONG).show();
- return;
- }
-
- TextView keyfileView = (TextView) rootView.findViewById(R.id.pass_keyfile);
- Uri keyfile = UriUtil.parseDefaultFile(keyfileView.getText().toString());
- mKeyfile = keyfile;
-
- // Verify that a password or keyfile is set
- if ( pass.length() == 0 && EmptyUtils.isNullOrEmpty(keyfile)) {
- Toast.makeText(getContext(), R.string.error_nopass, Toast.LENGTH_LONG).show();
- return;
-
- }
-
- SetPassword sp = new SetPassword(getContext(), App.getDB(), pass, keyfile, new AfterSave(mFinish, new Handler()));
- final ProgressTask pt = new ProgressTask(getContext(), sp, R.string.saving_database);
- boolean valid = sp.validatePassword(getContext(), new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- pt.run();
- }
- });
-
- if (valid) {
- pt.run();
- }
- }
- })
- .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- SetPasswordDialog.this.getDialog().cancel();
- if ( mFinish != null ) {
- mFinish.run();
- }
- }
- });
- return builder.create();
- }
-
- private class AfterSave extends OnFinish {
- private FileOnFinish mFinish;
-
- public AfterSave(FileOnFinish finish, Handler handler) {
- super(finish, handler);
- mFinish = finish;
- }
-
- @Override
- public void run() {
- if ( mSuccess ) {
- if ( mFinish != null ) {
- mFinish.setFilename(mKeyfile);
- }
- dismiss();
- } else {
- displayMessage(getContext());
- }
- super.run();
- }
- }
-}
diff --git a/app/src/main/java/com/keepassdroid/database/PwDatabaseV4.java b/app/src/main/java/com/keepassdroid/database/PwDatabaseV4.java
index 256ad3f09..c3ec8d122 100644
--- a/app/src/main/java/com/keepassdroid/database/PwDatabaseV4.java
+++ b/app/src/main/java/com/keepassdroid/database/PwDatabaseV4.java
@@ -19,12 +19,29 @@
*/
package com.keepassdroid.database;
-import java.io.FileInputStream;
+import android.webkit.URLUtil;
+
+import com.keepassdroid.collections.VariantDictionary;
+import com.keepassdroid.crypto.CryptoUtil;
+import com.keepassdroid.crypto.engine.AesEngine;
+import com.keepassdroid.crypto.engine.CipherEngine;
+import com.keepassdroid.crypto.keyDerivation.AesKdf;
+import com.keepassdroid.crypto.keyDerivation.KdfEngine;
+import com.keepassdroid.crypto.keyDerivation.KdfFactory;
+import com.keepassdroid.crypto.keyDerivation.KdfParameters;
+import com.keepassdroid.database.exception.InvalidKeyFileException;
+import com.keepassdroid.utils.EmptyUtils;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-import java.security.acl.Group;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
@@ -36,29 +53,8 @@ import java.util.UUID;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
-import org.spongycastle.crypto.engines.AESEngine;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.w3c.dom.Text;
-
-import android.webkit.URLUtil;
import biz.source_code.base64Coder.Base64Coder;
-import com.keepassdroid.collections.VariantDictionary;
-import com.keepassdroid.crypto.CipherFactory;
-import com.keepassdroid.crypto.CryptoUtil;
-import com.keepassdroid.crypto.PwStreamCipherFactory;
-import com.keepassdroid.crypto.engine.AesEngine;
-import com.keepassdroid.crypto.engine.CipherEngine;
-import com.keepassdroid.crypto.keyDerivation.AesKdf;
-import com.keepassdroid.crypto.keyDerivation.KdfEngine;
-import com.keepassdroid.crypto.keyDerivation.KdfFactory;
-import com.keepassdroid.crypto.keyDerivation.KdfParameters;
-import com.keepassdroid.database.exception.InvalidKeyFileException;
-import com.keepassdroid.utils.EmptyUtils;
-
public class PwDatabaseV4 extends PwDatabase {
@@ -131,7 +127,7 @@ public class PwDatabaseV4 extends PwDatabase {
throws InvalidKeyFileException, IOException {
assert(key != null);
- byte[] fKey;
+ byte[] fKey = new byte[]{};
if ( key.length() > 0 && keyInputStream != null) {
return getCompositeKey(key, keyInputStream);
@@ -139,8 +135,6 @@ public class PwDatabaseV4 extends PwDatabase {
fKey = getPasswordKey(key);
} else if ( keyInputStream != null) {
fKey = getFileKey(keyInputStream);
- } else {
- throw new IllegalArgumentException( "Key cannot be empty." );
}
MessageDigest md;
diff --git a/app/src/main/java/com/keepassdroid/database/edit/CreateDB.java b/app/src/main/java/com/keepassdroid/database/edit/CreateDB.java
index d59ae5e4c..5217e257f 100644
--- a/app/src/main/java/com/keepassdroid/database/edit/CreateDB.java
+++ b/app/src/main/java/com/keepassdroid/database/edit/CreateDB.java
@@ -19,20 +19,14 @@
*/
package com.keepassdroid.database.edit;
-
import android.content.Context;
-import android.net.Uri;
import com.keepassdroid.Database;
import com.keepassdroid.app.App;
import com.keepassdroid.database.PwDatabase;
-import com.keepassdroid.database.PwDatabaseV3;
-import com.keepassdroid.database.PwEncryptionAlgorithm;
import com.keepassdroid.utils.UriUtil;
public class CreateDB extends RunnableOnFinish {
-
- private final int DEFAULT_ENCRYPTION_ROUNDS = 300;
private String mFilename;
private boolean mDontSave;
@@ -57,7 +51,6 @@ public class CreateDB extends RunnableOnFinish {
// Set Database state
db.pm = pm;
- Uri.Builder b = new Uri.Builder();
db.mUri = UriUtil.parseDefaultFile(mFilename);
db.setLoaded();
App.clearShutdown();
diff --git a/app/src/main/java/com/keepassdroid/database/edit/SetPassword.java b/app/src/main/java/com/keepassdroid/database/edit/SetPassword.java
index 29ae6e39e..d19a5cfe0 100644
--- a/app/src/main/java/com/keepassdroid/database/edit/SetPassword.java
+++ b/app/src/main/java/com/keepassdroid/database/edit/SetPassword.java
@@ -19,9 +19,6 @@
*/
package com.keepassdroid.database.edit;
-import java.io.IOException;
-import java.io.InputStream;
-
import android.content.Context;
import android.content.DialogInterface;
import android.net.Uri;
@@ -32,6 +29,9 @@ import com.keepassdroid.database.exception.InvalidKeyFileException;
import com.keepassdroid.dialog.PasswordEncodingDialogHelper;
import com.keepassdroid.utils.UriUtil;
+import java.io.IOException;
+import java.io.InputStream;
+
public class SetPassword extends RunnableOnFinish {
private String mPassword;
diff --git a/app/src/main/java/com/keepassdroid/fileselect/FileSelectActivity.java b/app/src/main/java/com/keepassdroid/fileselect/FileSelectActivity.java
index 96c502ee4..224947c9f 100644
--- a/app/src/main/java/com/keepassdroid/fileselect/FileSelectActivity.java
+++ b/app/src/main/java/com/keepassdroid/fileselect/FileSelectActivity.java
@@ -19,8 +19,8 @@
*/
package com.keepassdroid.fileselect;
-
import android.Manifest;
+import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Intent;
@@ -35,6 +35,7 @@ import android.preference.PreferenceManager;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.Toolbar;
+import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
@@ -44,18 +45,16 @@ import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
-import android.widget.Button;
import android.widget.EditText;
-import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
-import com.kunzisoft.keepass.R;
+import com.keepassdroid.AssignMasterKeyDialog;
+import com.keepassdroid.CreateFileDialog;
import com.keepassdroid.GroupActivity;
import com.keepassdroid.PasswordActivity;
import com.keepassdroid.ProgressTask;
-import com.keepassdroid.SetPasswordDialog;
import com.keepassdroid.app.App;
import com.keepassdroid.compat.ContentResolverCompat;
import com.keepassdroid.compat.StorageAF;
@@ -69,18 +68,25 @@ import com.keepassdroid.utils.Interaction;
import com.keepassdroid.utils.MenuUtil;
import com.keepassdroid.utils.UriUtil;
import com.keepassdroid.utils.Util;
+import com.keepassdroid.view.AssignPasswordHelper;
import com.keepassdroid.view.FileNameView;
+import com.kunzisoft.keepass.R;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.lang.ref.WeakReference;
import java.net.URLDecoder;
-public class FileSelectActivity extends StylishActivity {
+public class FileSelectActivity extends StylishActivity implements
+ CreateFileDialog.DefinePathDialogListener ,
+ AssignMasterKeyDialog.AssignPasswordDialogListener {
+
+ private static final String TAG = "FileSelectActivity";
private static final int MY_PERMISSIONS_REQUEST_EXTERNAL_STORAGE = 111;
private ListView mList;
- private ListAdapter mAdapter;
+ private BaseAdapter mAdapter;
private static final int CMENU_CLEAR = Menu.FIRST;
@@ -92,18 +98,23 @@ public class FileSelectActivity extends StylishActivity {
private boolean recentMode = false;
+ private EditText openFileNameView;
+
+ private AssignPasswordHelper assignPasswordHelper;
+ private Uri databaseUri;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fileHistory = App.getFileHistory();
+ setContentView(R.layout.file_selection);
if (fileHistory.hasRecentFiles()) {
recentMode = true;
- setContentView(R.layout.file_selection);
} else {
- setContentView(R.layout.file_selection_no_recent);
- }
+ findViewById(R.id.file_listtop).setVisibility(View.INVISIBLE);
+ }
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle(getString(R.string.app_name));
@@ -112,22 +123,20 @@ public class FileSelectActivity extends StylishActivity {
mList = (ListView)findViewById(R.id.file_list);
mList.setOnItemClickListener(
- new AdapterView.OnItemClickListener() {
- public void onItemClick(AdapterView> parent, View v, int position, long id)
- {
- onListItemClick((ListView)parent, v, position, id);
- }
- }
+ new AdapterView.OnItemClickListener() {
+ public void onItemClick(AdapterView> parent, View v, int position, long id) {
+ onListItemClick((ListView)parent, v, position, id);
+ }
+ }
);
// Open button
- Button openButton = (Button) findViewById(R.id.open);
+ View openButton = findViewById(R.id.open_database);
openButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
String fileName = Util.getEditText(FileSelectActivity.this,
R.id.file_filename);
-
try {
PasswordActivity.Launch(FileSelectActivity.this, fileName);
}
@@ -139,80 +148,16 @@ public class FileSelectActivity extends StylishActivity {
Toast.makeText(FileSelectActivity.this,
R.string.FileNotFound, Toast.LENGTH_LONG).show();
}
-
}
});
// Create button
- Button createButton = (Button) findViewById(R.id.create);
+ View createButton = findViewById(R.id.create_database);
createButton.setOnClickListener(new View.OnClickListener() {
-
public void onClick(View v) {
- String filename = Util.getEditText(FileSelectActivity.this,
- R.id.file_filename);
-
- // Make sure file name exists
- if (filename.length() == 0) {
- Toast
- .makeText(FileSelectActivity.this,
- R.string.error_filename_required,
- Toast.LENGTH_LONG).show();
- return;
- }
-
- // Try to create the file
- File file = new File(filename);
- try {
- if (file.exists()) {
- Toast.makeText(FileSelectActivity.this,
- R.string.error_database_exists,
- Toast.LENGTH_LONG).show();
- return;
- }
- File parent = file.getParentFile();
-
- if ( parent == null || (parent.exists() && ! parent.isDirectory()) ) {
- Toast.makeText(FileSelectActivity.this,
- R.string.error_invalid_path,
- Toast.LENGTH_LONG).show();
- return;
- }
-
- if ( ! parent.exists() ) {
- // Create parent dircetory
- if ( ! parent.mkdirs() ) {
- Toast.makeText(FileSelectActivity.this,
- R.string.error_could_not_create_parent,
- Toast.LENGTH_LONG).show();
- return;
-
- }
- }
-
- file.createNewFile();
- } catch (IOException e) {
- Toast.makeText(
- FileSelectActivity.this,
- getText(R.string.error_file_not_create) + " "
- + e.getLocalizedMessage(),
- Toast.LENGTH_LONG).show();
- return;
- }
-
- // Prep an object to collect a password once the database has
- // been created
- CollectPassword password = new CollectPassword(
- new LaunchGroupActivity(filename));
-
- // Create the new database
- CreateDB create = new CreateDB(FileSelectActivity.this, filename, password, true);
- ProgressTask createTask = new ProgressTask(
- FileSelectActivity.this, create,
- R.string.progress_create);
- createTask.run();
-
+ CreateFileDialog createFileDialog = new CreateFileDialog();
+ createFileDialog.show(getSupportFragmentManager(), "createFileDialog");
}
-
});
View browseButton = findViewById(R.id.browse_button);
@@ -223,7 +168,9 @@ public class FileSelectActivity extends StylishActivity {
Intent i = new Intent(StorageAF.ACTION_OPEN_DOCUMENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
- i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION|Intent.FLAG_GRANT_WRITE_URI_PERMISSION|Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
+ i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION|
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION|
+ Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
startActivityForResult(i, OPEN_DOC);
}
else {
@@ -247,7 +194,6 @@ public class FileSelectActivity extends StylishActivity {
}
private void lookForOpenIntentsFilePicker() {
-
if (Interaction.isIntentAvailable(FileSelectActivity.this, Intents.OPEN_INTENTS_FILE_BROWSE)) {
Intent i = new Intent(Intents.OPEN_INTENTS_FILE_BROWSE);
i.setData(Uri.parse("file://" + Util.getEditText(FileSelectActivity.this, R.id.file_filename)));
@@ -256,7 +202,6 @@ public class FileSelectActivity extends StylishActivity {
} catch (ActivityNotFoundException e) {
showBrowserDialog();
}
-
} else {
showBrowserDialog();
}
@@ -268,6 +213,14 @@ public class FileSelectActivity extends StylishActivity {
}
});
+ // Set the initial value of the filename
+ openFileNameView = (EditText) findViewById(R.id.file_filename);
+ String defaultPath = Environment.getExternalStorageDirectory().getAbsolutePath()
+ + getString(R.string.database_file_path_default)
+ + getString(R.string.database_file_name_default)
+ + getString(R.string.database_file_extension_default);
+ openFileNameView.setText(defaultPath);
+
fillData();
registerForContextMenu(mList);
@@ -278,7 +231,9 @@ public class FileSelectActivity extends StylishActivity {
if (fileName.length() > 0) {
Uri dbUri = UriUtil.parseDefaultFile(fileName);
- String scheme = dbUri.getScheme();
+ String scheme = null;
+ if (dbUri!=null)
+ scheme = dbUri.getScheme();
if (!EmptyUtils.isNullOrEmpty(scheme) && scheme.equalsIgnoreCase("file")) {
String path = dbUri.getPath();
@@ -302,12 +257,135 @@ public class FileSelectActivity extends StylishActivity {
}
}
+ /**
+ * Create file for database
+ * @return If not created, return false
+ */
+ private boolean createDatabaseFile(Uri path) {
+
+ String pathString = URLDecoder.decode(path.getPath());
+ // Make sure file name exists
+ if (pathString.length() == 0) {
+ Log.e(TAG, getString(R.string.error_filename_required));
+ Toast.makeText(FileSelectActivity.this,
+ R.string.error_filename_required,
+ Toast.LENGTH_LONG).show();
+ return false;
+ }
+
+ // Try to create the file
+ File file = new File(pathString);
+ try {
+ if (file.exists()) {
+ Log.e(TAG, getString(R.string.error_database_exists) + " " + file);
+ Toast.makeText(FileSelectActivity.this,
+ R.string.error_database_exists,
+ Toast.LENGTH_LONG).show();
+ return false;
+ }
+ File parent = file.getParentFile();
+
+ if ( parent == null || (parent.exists() && ! parent.isDirectory()) ) {
+ Log.e(TAG, getString(R.string.error_invalid_path) + " " + file);
+ Toast.makeText(FileSelectActivity.this,
+ R.string.error_invalid_path,
+ Toast.LENGTH_LONG).show();
+ return false;
+ }
+
+ if ( ! parent.exists() ) {
+ // Create parent directory
+ if ( ! parent.mkdirs() ) {
+ Log.e(TAG, getString(R.string.error_could_not_create_parent) + " " + parent);
+ Toast.makeText(FileSelectActivity.this,
+ R.string.error_could_not_create_parent,
+ Toast.LENGTH_LONG).show();
+ return false;
+ }
+ }
+
+ return file.createNewFile();
+ } catch (IOException e) {
+ Log.e(TAG, getString(R.string.error_could_not_create_parent) + " " + e.getLocalizedMessage());
+ e.printStackTrace();
+ Toast.makeText(
+ FileSelectActivity.this,
+ getText(R.string.error_file_not_create) + " "
+ + e.getLocalizedMessage(),
+ Toast.LENGTH_LONG).show();
+ return false;
+ }
+ }
+
+ @Override
+ public boolean onDefinePathDialogPositiveClick(Uri pathFile) {
+ databaseUri = pathFile;
+ if(createDatabaseFile(pathFile)) {
+ AssignMasterKeyDialog assignMasterKeyDialog = new AssignMasterKeyDialog();
+ assignMasterKeyDialog.show(getSupportFragmentManager(), "passwordDialog");
+ return true;
+ } else
+ return false;
+ }
+
+ @Override
+ public boolean onDefinePathDialogNegativeClick(Uri pathFile) {
+ return true;
+ }
+
+ @Override
+ public void onAssignKeyDialogPositiveClick(
+ boolean masterPasswordChecked, String masterPassword,
+ boolean keyFileChecked, Uri keyFile) {
+
+ String databaseFilename = databaseUri.getPath();
+
+ // Prep an object to collect a password once the database has
+ // been created
+ FileOnFinish launchActivityOnFinish = new FileOnFinish(
+ new LaunchGroupActivity(databaseFilename));
+ AssignPasswordOnFinish assignPasswordOnFinish =
+ new AssignPasswordOnFinish(launchActivityOnFinish);
+
+ // Create the new database
+ CreateDB create = new CreateDB(FileSelectActivity.this,
+ databaseFilename, assignPasswordOnFinish, true);
+
+ ProgressTask createTask = new ProgressTask(
+ FileSelectActivity.this, create,
+ R.string.progress_create);
+ createTask.run();
+ assignPasswordHelper =
+ new AssignPasswordHelper(this,
+ masterPassword, keyFile);
+ }
+
+ @Override
+ public void onAssignKeyDialogNegativeClick(
+ boolean masterPasswordChecked, String masterPassword,
+ boolean keyFileChecked, Uri keyFile) {
+
+ }
+
+ private class AssignPasswordOnFinish extends FileOnFinish {
+
+ AssignPasswordOnFinish(FileOnFinish fileOnFinish) {
+ super(fileOnFinish);
+ }
+
+ @Override
+ public void run() {
+ if (mSuccess) {
+ assignPasswordHelper.assignPasswordInDatabase(mOnFinish);
+ }
+ }
+ }
+
private class LaunchGroupActivity extends FileOnFinish {
private Uri mUri;
- public LaunchGroupActivity(String filename) {
+ LaunchGroupActivity(String filename) {
super(null);
-
mUri = UriUtil.parseDefaultFile(filename);
}
@@ -316,61 +394,19 @@ public class FileSelectActivity extends StylishActivity {
if (mSuccess) {
// Add to recent files
fileHistory.createFile(mUri, getFilename());
-
+ mAdapter.notifyDataSetChanged();
GroupActivity.Launch(FileSelectActivity.this);
}
}
}
- private class CollectPassword extends FileOnFinish {
-
- public CollectPassword(FileOnFinish finish) {
- super(finish);
- }
-
- @Override
- public void run() {
- SetPasswordDialog dialog = SetPasswordDialog.newInstance(mOnFinish);
- dialog.show(getSupportFragmentManager(), "passwordDialog");
- }
-
- }
-
private void fillData() {
- // Set the initial value of the filename
- EditText filename = (EditText) findViewById(R.id.file_filename);
- filename.setText(Environment.getExternalStorageDirectory().getAbsolutePath() + getString(R.string.default_file_path));
-
- mAdapter = new ArrayAdapter(this, R.layout.file_row, R.id.file_filename, fileHistory.getDbList());
- mList.setAdapter(mAdapter);
+ mAdapter = new ArrayAdapter<>(FileSelectActivity.this, R.layout.file_row, R.id.file_filename, fileHistory.getDbList());
+ mList.setAdapter(mAdapter);
}
protected void onListItemClick(ListView l, View v, int position, long id) {
-
- new AsyncTask() {
- String fileName;
- String keyFile;
- protected Void doInBackground(Integer... args) {
- int position = args[0];
- fileName = fileHistory.getDatabaseAt(position);
- keyFile = fileHistory.getKeyfileAt(position);
- return null;
- }
-
- protected void onPostExecute(Void v) {
- try {
- PasswordActivity.Launch(FileSelectActivity.this, fileName, keyFile);
- }
- catch (ContentFileNotFoundException e) {
- Toast.makeText(FileSelectActivity.this, R.string.file_not_found_content, Toast.LENGTH_LONG)
- .show();
- }
- catch (FileNotFoundException e) {
- Toast.makeText(FileSelectActivity.this, R.string.FileNotFound, Toast.LENGTH_LONG)
- .show();
- }
- }
- }.execute(position);
+ new OpenFileHistoryAsyncTask(this, fileHistory).execute(position);
}
@Override
@@ -414,8 +450,7 @@ public class FileSelectActivity extends StylishActivity {
}
if (filename != null) {
- EditText fn = (EditText) findViewById(R.id.file_filename);
- fn.setText(filename);
+ openFileNameView.setText(filename);
}
}
@@ -522,25 +557,65 @@ public class FileSelectActivity extends StylishActivity {
TextView tv = (TextView) acmi.targetView;
String filename = tv.getText().toString();
- new AsyncTask() {
- protected java.lang.Void doInBackground(String... args) {
- String filename = args[0];
- fileHistory.deleteFile(Uri.parse(args[0]));
- return null;
- }
-
- protected void onPostExecute(Void v) {
- refreshList();
- }
- }.execute(filename);
+ new DeleteFileHistoryAsyncTask(fileHistory, mAdapter).execute(filename);
return true;
}
return false;
}
-
- private void refreshList() {
- ((BaseAdapter) mAdapter).notifyDataSetChanged();
- }
+
+ private static class OpenFileHistoryAsyncTask extends AsyncTask {
+
+ private WeakReference weakActivity;
+ private RecentFileHistory fileHistory;
+ private String fileName;
+ private String keyFile;
+
+ OpenFileHistoryAsyncTask(Activity activity, RecentFileHistory fileHistory) {
+ this.weakActivity = new WeakReference<>(activity);
+ this.fileHistory = fileHistory;
+ }
+
+ protected Void doInBackground(Integer... args) {
+ int position = args[0];
+ fileName = fileHistory.getDatabaseAt(position);
+ keyFile = fileHistory.getKeyfileAt(position);
+ return null;
+ }
+
+ protected void onPostExecute(Void v) {
+ try {
+ PasswordActivity.Launch(weakActivity.get(), fileName, keyFile);
+ }
+ catch (ContentFileNotFoundException e) {
+ Toast.makeText(weakActivity.get(), R.string.file_not_found_content, Toast.LENGTH_LONG)
+ .show();
+ }
+ catch (FileNotFoundException e) {
+ Toast.makeText(weakActivity.get(), R.string.FileNotFound, Toast.LENGTH_LONG)
+ .show();
+ }
+ }
+ }
+
+ private static class DeleteFileHistoryAsyncTask extends AsyncTask {
+
+ private RecentFileHistory fileHistory;
+ private BaseAdapter adapter;
+
+ DeleteFileHistoryAsyncTask(RecentFileHistory fileHistory, BaseAdapter adapter) {
+ this.fileHistory = fileHistory;
+ this.adapter = adapter;
+ }
+
+ protected java.lang.Void doInBackground(String... args) {
+ fileHistory.deleteFile(Uri.parse(args[0]));
+ return null;
+ }
+
+ protected void onPostExecute(Void v) {
+ adapter.notifyDataSetChanged();
+ }
+ }
}
diff --git a/app/src/main/java/com/keepassdroid/stylish/Stylish.java b/app/src/main/java/com/keepassdroid/stylish/Stylish.java
index 3ef7f3e5d..abb93d8ad 100644
--- a/app/src/main/java/com/keepassdroid/stylish/Stylish.java
+++ b/app/src/main/java/com/keepassdroid/stylish/Stylish.java
@@ -9,9 +9,9 @@ import com.kunzisoft.keepass.R;
public class Stylish {
- private static String stylishPrefKey;
+ protected static String stylishPrefKey;
- private static String themeString;
+ protected static String themeString;
public static void init(Context context) {
stylishPrefKey = context.getString(R.string.setting_style_key);
@@ -23,7 +23,7 @@ public class Stylish {
themeString = styleString;
}
- static @StyleRes int getThemeId(Context context) {
+ public static @StyleRes int getThemeId(Context context) {
if (themeString.equals(context.getString(R.string.list_style_name_night)))
return R.style.KeepassDXStyle_Night;
diff --git a/app/src/main/java/com/keepassdroid/utils/Util.java b/app/src/main/java/com/keepassdroid/utils/Util.java
index 707a639ca..ffb0d7adb 100644
--- a/app/src/main/java/com/keepassdroid/utils/Util.java
+++ b/app/src/main/java/com/keepassdroid/utils/Util.java
@@ -19,12 +19,6 @@
*/
package com.keepassdroid.utils;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import com.keepassdroid.database.exception.SamsungClipboardException;
-
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
@@ -33,6 +27,12 @@ import android.net.Uri;
import android.text.ClipboardManager;
import android.widget.TextView;
+import com.keepassdroid.database.exception.SamsungClipboardException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
public class Util {
public static String getClipboard(Context context) {
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
@@ -75,14 +75,6 @@ public class Util {
}
}
- public static void setEditText(Activity act, int resId, String str) {
- TextView te = (TextView) act.findViewById(resId);
-
- if (te != null) {
- te.setText(str);
- }
- }
-
public static void copyStream(InputStream in, OutputStream out) throws IOException {
byte[] buf = new byte[1024];
int read;
diff --git a/app/src/main/java/com/keepassdroid/view/AssignPasswordHelper.java b/app/src/main/java/com/keepassdroid/view/AssignPasswordHelper.java
new file mode 100644
index 000000000..878957f8d
--- /dev/null
+++ b/app/src/main/java/com/keepassdroid/view/AssignPasswordHelper.java
@@ -0,0 +1,67 @@
+package com.keepassdroid.view;
+
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.net.Uri;
+import android.os.Handler;
+
+import com.keepassdroid.ProgressTask;
+import com.keepassdroid.app.App;
+import com.keepassdroid.database.edit.FileOnFinish;
+import com.keepassdroid.database.edit.OnFinish;
+import com.keepassdroid.database.edit.SetPassword;
+import com.kunzisoft.keepass.R;
+
+public class AssignPasswordHelper {
+
+ private Context context;
+
+ private String masterPassword;
+ private Uri keyfile;
+
+ public AssignPasswordHelper(Context context,
+ String masterPassword,
+ Uri keyfile) {
+ this.context = context;
+ this.masterPassword = masterPassword;
+ this.keyfile = keyfile;
+ }
+
+ public void assignPasswordInDatabase(FileOnFinish fileOnFinish) {
+ SetPassword sp = new SetPassword(context, App.getDB(), masterPassword, keyfile, new AfterSave(fileOnFinish, new Handler()));
+ final ProgressTask pt = new ProgressTask(context, sp, R.string.saving_database);
+ boolean valid = sp.validatePassword(context, new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ pt.run();
+ }
+ });
+
+ if (valid) {
+ pt.run();
+ }
+ }
+
+ private class AfterSave extends OnFinish {
+ private FileOnFinish mFinish;
+
+ public AfterSave(FileOnFinish finish, Handler handler) {
+ super(finish, handler);
+ mFinish = finish;
+ }
+
+ @Override
+ public void run() {
+ if ( mSuccess ) {
+ if ( mFinish != null ) {
+ mFinish.setFilename(keyfile);
+ }
+ } else {
+ displayMessage(context);
+ }
+ super.run();
+ }
+ }
+}
diff --git a/app/src/main/java/com/keepassdroid/view/KeyFileHelper.java b/app/src/main/java/com/keepassdroid/view/KeyFileHelper.java
new file mode 100644
index 000000000..5e7d570f6
--- /dev/null
+++ b/app/src/main/java/com/keepassdroid/view/KeyFileHelper.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2017 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.keepassdroid.view;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.net.Uri;
+import android.support.v4.app.Fragment;
+import android.view.View;
+
+import com.keepassdroid.compat.StorageAF;
+import com.keepassdroid.fileselect.BrowserDialog;
+import com.keepassdroid.intents.Intents;
+import com.keepassdroid.utils.Interaction;
+import com.keepassdroid.utils.UriUtil;
+
+import java.io.File;
+
+import static android.app.Activity.RESULT_OK;
+
+
+public class KeyFileHelper {
+
+ public static final int GET_CONTENT = 25745;
+ private static final int OPEN_DOC = 25845;
+ private static final int FILE_BROWSE = 25645;
+
+ private Activity activity;
+ private Fragment fragment;
+ private Uri mDbUri;
+
+ public KeyFileHelper(Activity context) {
+ this(context, null);
+ }
+
+ public KeyFileHelper(Activity context, Uri mDbUri) {
+ this.activity = context;
+ this.fragment = null;
+ this.mDbUri = mDbUri;
+ }
+
+ public KeyFileHelper(Fragment context) {
+ this(context, null);
+ }
+
+ public KeyFileHelper(Fragment context, Uri mDbUri) {
+ this.activity = context.getActivity();
+ this.fragment = context;
+ this.mDbUri = mDbUri;
+ }
+
+ public View.OnClickListener getOpenFileOnClickViewListener() {
+ return new View.OnClickListener() {
+
+ public void onClick(View v) {
+ if (StorageAF.useStorageFramework(activity)) {
+ Intent i = new Intent(StorageAF.ACTION_OPEN_DOCUMENT);
+ i.addCategory(Intent.CATEGORY_OPENABLE);
+ i.setType("*/*");
+ if(fragment != null)
+ fragment.startActivityForResult(i, OPEN_DOC);
+ else
+ activity.startActivityForResult(i, OPEN_DOC);
+ } else {
+ Intent i = new Intent(Intent.ACTION_GET_CONTENT);
+ i.addCategory(Intent.CATEGORY_OPENABLE);
+ i.setType("*/*");
+
+ try {
+ if(fragment != null)
+ fragment.startActivityForResult(i, GET_CONTENT);
+ else
+ activity.startActivityForResult(i, GET_CONTENT);
+ } catch (ActivityNotFoundException e) {
+ lookForOpenIntentsFilePicker();
+ }
+ }
+ }
+ };
+ }
+
+ private void lookForOpenIntentsFilePicker() {
+ if (Interaction.isIntentAvailable(activity, 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 {
+ if(fragment != null)
+ fragment.startActivityForResult(i, FILE_BROWSE);
+ else
+ activity.startActivityForResult(i, FILE_BROWSE);
+ } catch (ActivityNotFoundException e) {
+ showBrowserDialog();
+ }
+ } else {
+ showBrowserDialog();
+ }
+ }
+
+ private void showBrowserDialog() {
+ BrowserDialog browserDialog = new BrowserDialog(activity);
+ browserDialog.show();
+ }
+
+
+ public void onActivityResultCallback(
+ int requestCode,
+ int resultCode,
+ Intent data,
+ KeyFileCallback keyFileCallback) {
+
+ switch (requestCode) {
+ case FILE_BROWSE:
+ if (resultCode == RESULT_OK) {
+ String filename = data.getDataString();
+ Uri keyUri = null;
+ if (filename != null) {
+ keyUri = UriUtil.parseDefaultFile(filename);
+ }
+ if (keyFileCallback != null)
+ keyFileCallback.onKeyFileResultCallback(keyUri);
+ }
+ 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(activity, uri);
+ }
+ if (keyFileCallback != null)
+ keyFileCallback.onKeyFileResultCallback(uri);
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ public interface KeyFileCallback {
+ void onKeyFileResultCallback(Uri uri);
+ }
+
+}
diff --git a/app/src/main/res/drawable/ic_database_plus_white_24dp.xml b/app/src/main/res/drawable/ic_database_plus_white_24dp.xml
new file mode 100644
index 000000000..41d0311db
--- /dev/null
+++ b/app/src/main/res/drawable/ic_database_plus_white_24dp.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_open_folder_white_24dp.xml b/app/src/main/res/drawable/ic_open_folder_white_24dp.xml
new file mode 100644
index 000000000..ff6b4690f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_open_folder_white_24dp.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/file_creation.xml b/app/src/main/res/layout/file_creation.xml
new file mode 100644
index 000000000..b438658df
--- /dev/null
+++ b/app/src/main/res/layout/file_creation.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/file_selection.xml b/app/src/main/res/layout/file_selection.xml
index f812956c6..df040805e 100644
--- a/app/src/main/res/layout/file_selection.xml
+++ b/app/src/main/res/layout/file_selection.xml
@@ -17,32 +17,47 @@
You should have received a copy of the GNU General Public License
along with KeePass DX. If not, see .
-->
-
-
-
-
-
+
+ android:layout_height="match_parent"
+ android:orientation="vertical">
-
+
+
+
+
+
+
+
+
+
+
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentBottom="true"
+ android:src="@drawable/ic_database_plus_white_24dp"
+ style="@style/KeepassDXStyle.Fab"/>
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/file_selection_filename.xml b/app/src/main/res/layout/file_selection_filename.xml
index b75656729..049f503b7 100644
--- a/app/src/main/res/layout/file_selection_filename.xml
+++ b/app/src/main/res/layout/file_selection_filename.xml
@@ -17,62 +17,76 @@
You should have received a copy of the GNU General Public License
along with KeePass DX. If not, see .
-->
-
+
-
-
-
-
-
-
-
+ android:layout_marginBottom="32dp">
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/file_selection_no_recent.xml b/app/src/main/res/layout/file_selection_no_recent.xml
deleted file mode 100644
index 03bd789bd..000000000
--- a/app/src/main/res/layout/file_selection_no_recent.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/password.xml b/app/src/main/res/layout/password.xml
index 0812797c9..22c00df31 100644
--- a/app/src/main/res/layout/password.xml
+++ b/app/src/main/res/layout/password.xml
@@ -59,66 +59,73 @@
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
- android:layout_below="@+id/toolbar"
- android:layout_above="@+id/pass_ok">
+ android:layout_below="@+id/toolbar">
-
+
+
+
+
+
+ android:layout_centerVertical="true"
+ android:layout_toLeftOf="@+id/fingerprint_image"
+ android:layout_toStartOf="@+id/fingerprint_image"
+ android:gravity="center_vertical|end"
+ android:text="@string/entry_and_or"
+ android:textColor="?attr/colorAccent"
+ tools:visibility="visible" />
+
-
-
+
+
+
-
-
+ android:layout_alignParentBottom="true"
+ android:paddingBottom="20dp"
+ android:gravity="center_vertical" />
-
-
-
-
-
@@ -130,8 +137,21 @@
android:inputType="textPassword"
android:maxLines="1"/>
+
+
+
+
+
+
-
+
+ android:layout_toEndOf="@+id/keyfile_checkox"
+ android:layout_toRightOf="@+id/keyfile_checkox"
+ android:layout_toLeftOf="@+id/browse_button"
+ android:layout_toStartOf="@+id/browse_button">
+ android:maxLines="1" />
-
-
+
diff --git a/app/src/main/res/layout/set_password.xml b/app/src/main/res/layout/set_password.xml
index 478e0aa40..44f0cccd9 100644
--- a/app/src/main/res/layout/set_password.xml
+++ b/app/src/main/res/layout/set_password.xml
@@ -17,27 +17,109 @@
You should have received a copy of the GNU General Public License
along with KeePass DX. If not, see .
-->
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml
index 319c3cf87..97ac09c7b 100644
--- a/app/src/main/res/values-ca/strings.xml
+++ b/app/src/main/res/values-ca/strings.xml
@@ -61,7 +61,7 @@
Confirma contrasenya:
Creada:
Expira:
- Arxiu clau (opcional)
+ Arxiu clau
Modificada:
Contrasenya:
Guarda
@@ -80,7 +80,7 @@
Base de dades invàlida.
Camí invàlid.
És necessari un nom.
- És necessaria una contrasenya o un arxiu clau.
+ És necessaria una contrasenya o un arxiu clau.
El telèfon sha quedat sense memòria processant la teva base de dades. Potser és massa gran pel teu telèfon.
Has de seleccionar almenys un tipus de generador de contrasenyes
Les contrasenyes no coincideixen.
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 50d4c9e8d..a96b5eed5 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -66,7 +66,7 @@
Potvrďte heslo:
Vytvořeno:
Vyprší:
- Klíčový soubor (nepovinné)
+ Klíčový soubor
Změněno:
Vstupní data nenalezena.
Heslo:
@@ -86,7 +86,7 @@
Chybná databáze.
Chybná cesta.
Jméno je povinné.
- Heslo nebo klíčový soubor jsou povinné.
+ Heslo nebo klíčový soubor jsou povinné.
Přístroj má málo paměti pro zpracování databáze. Možná je příliš velká pro Váš přístroj.
Minimálně jeden typ generování hesla musí být zvolen
Hesla se neshodují.
diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml
index 22be9b8a7..b30511d88 100644
--- a/app/src/main/res/values-da/strings.xml
+++ b/app/src/main/res/values-da/strings.xml
@@ -65,7 +65,7 @@
Bekræft adgangskode:
Oprettet:
Udløber:
- Nøglefil (valgfri)
+ Nøglefil
Ændret:
Data for posten blev ikke fundet.
Adgangskode:
@@ -85,7 +85,7 @@
Ugyldig database.
Ugyldig sti.
Et navn er påkrævet.
- En adgangskode eller nøglefil er påkrævet.
+ En adgangskode eller nøglefil er påkrævet.
Telefonen løb tør for hukommelse under analysen af din database. Den kan være for stor til, at din telefon kan håndtere den.
Mindst én adgangskode-genererings-type skal vælges
Adgangskoder matcher ikke.
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 3f33debc3..1da73fd46 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -69,7 +69,7 @@
Passwort wiederholen:
Erstelldatum:
Ablaufdatum:
- Schlüsseldatei (optional)
+ Schlüsseldatei
Änderungsdatum:
Eintrag wurde nicht gefunden.
Passwort:
@@ -89,7 +89,7 @@
Ungültige Datenbank.
Ungültiger Pfad.
Ein Name wird benötigt.
- Ein Passwort oder eine Schlüsseldatei wird benötigt.
+ Ein Passwort oder eine Schlüsseldatei wird benötigt.
Der Speicher Ihres Smartphones reicht nicht aus, um die Datenbank zu analysieren. Sie ist wahrscheinlich zu groß.
Mindestens ein Passwort-Generierungstyp muss ausgewählt werden.
Die Passwörter stimmen nicht überein.
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index db69cc70b..7cf31f6cf 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -63,7 +63,7 @@
Επιβεβαίωση κωδικού πρόσβασης:
Δημιουργήθηκε:
Λήγει:
- Αρχείο Κλειδιού (προαιρετικό)
+ Αρχείο Κλειδιού
Τροποποιήθηκε:
Δεν βρέθηκαν δεδομένα εγγραφών.
Κωδικός Πρόσβασης:
@@ -83,7 +83,7 @@
Μη έγκυρη βάση δεδομένων.
Μη έγκυρη διαδρομή.
Απαιτείται ένα όνομα.
- Απαιτείται ο κωδικός πρόσβασης ή ένα αρχείο κλειδιού.
+ Απαιτείται ο κωδικός πρόσβασης ή ένα αρχείο κλειδιού.
Το τηλέφωνο ξέμεινε από μνήμη κατά τη διάρκεια προσπέλασης της βάσης δεδομένων σας. Μπορεί να είναι πολύ μεγάλη για το τηλέφωνο σας.
Πρέπει να επιλεγεί τουλάχιστον ένας τύπος δημιουργίας κωδικού πρόσβασης
Οι κωδικοί δεν ταιριάζουν.
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 4bc329663..f17fc3211 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -60,7 +60,7 @@ Spanish translation by José I. Paños. Updated by David García-Abad (23-09-201
Confirmar contraseña:
Creación:
Caducidad:
- Archivo de clave (opcional)
+ Archivo de clave
Modificación:
Contraseña:
Guardar
@@ -79,7 +79,7 @@ Spanish translation by José I. Paños. Updated by David García-Abad (23-09-201
Base de datos no válida.
Ruta no válida.
Se necesita un nombre.
- Se necesita una contraseña o un archivo de clave.
+ Se necesita una contraseña o un archivo de clave.
El dispositivo se quedó sin memory mientras analizada la base de datos. Puede ser demasiado grande para este dispositivo.
Debe seleccionar al menos un tipo de generación de contraseñas
Las contraseñas no coinciden.
diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml
index 9386fa679..1dc37a254 100644
--- a/app/src/main/res/values-eu/strings.xml
+++ b/app/src/main/res/values-eu/strings.xml
@@ -65,7 +65,7 @@
Pasahitza berretsi:
Sortua:
Iraungitzen da:
- Gako fitxategia (aukerazkoa)
+ Gako fitxategia
Aldatua:
Sarreraren datuak ez aurkituak.
Pasahitza:
@@ -85,7 +85,7 @@
Datubase baliogabea.
Fitxategirako bide baliogabea.
Izen bat behar da.
- Pasahitz edo gako fitxategi bat beharrezkoak dira.
+ Pasahitz edo gako fitxategi bat beharrezkoak dira.
Telefonoa memoriarik gabe gelditu da zure datubasea arakatzean. Handiegia izan daiteke zure telefonorako.
Pasahitza sortzeko mota bat gutxienez aukeratu behar da
Pasahitzak ez datoz bat.
diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml
index 2949d9b10..deed27c46 100644
--- a/app/src/main/res/values-fi/strings.xml
+++ b/app/src/main/res/values-fi/strings.xml
@@ -63,7 +63,7 @@
Vahvista salasana:
Luotu:
Vanhenee:
- Avaintiedosto (valinnainen)
+ Avaintiedosto
Muokattu:
Tietueen tietoja ei löytynyt.
Salasana:
@@ -83,7 +83,7 @@
Viallinen salasanatietokanta.
Viallinen hakemistopolku.
Nimi puuttuu.
- Salasana tai avaintiedosto puuttuu.
+ Salasana tai avaintiedosto puuttuu.
Puhelimesta loppui muisti salasanatietokantaa avatessa. Tietokanta voi olla liian suuri tälle puhelinmallille.
Vähintään yksi salasanagenerointitapa täytyy olla valittuna.
Salasanat eivät täsmää.
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 026a3613b..ce58686fa 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -66,7 +66,7 @@
Confirmer mot de passe
Créé
Expire
- Clé (facultative)
+ Fichier clé
Modifié
Entry data not found.
Mot de passe
@@ -86,7 +86,7 @@
Base de données invalide.
Chemin invalide.
Le nom est obligatoire.
- Un mot de passe ou clé de fichier est requis.
+ Un fichier clé est requis.
Votre appareil ne dispose pas de suffisamment de mémoire pour ouvrir votre base de données. Elle est probablement trop grosse pour votre appareil.
Au moins un type de génération de mot de passe doit être sélectionné
Les mots de passe ne correspondent pas.
@@ -191,6 +191,8 @@
Le format .kdb ne supporte que le jeu de caractère Latin1. Votre mot de passe doit contenir des caractères en dehors de ce jeu. Tous les caractères non-Latin1 sont convertis en un même caractère, ce qui diminue la sécurité de votre mot de passe. Le changement de votre mot de passe est recommandé.
Votre carte SD est actuellement en lecture seule. Vous ne pourrez pas enregistrer les changements dans la base de données.
Votre carte SD n\'est actuellement pas montée sur votre appareil. Vous ne pourrez pas charger ou créer votre base de données.
+ Voulez-vous vraiment utiliser une chaine de caractères vide comme mot de passe ?
+ Etes-vous sûr de ne vouloir utiliser aucune clé de chiffrement.
Version\u00A0:
Reconnaissance d\'empreinte digitale non configuré pour cet appareil
Attente d\'une reconnaissance d\'empreinte digitale
@@ -221,6 +223,10 @@
Écoute d\'empreintes digitales
Activer l\'ouverture de la base de données par empreinte digitale
Impossible de démarrer cette fonctionnalité\nVotre version Android %1$d n\'est pas la version minimale %2$d requise
+ Nom de fichier
+ Chemin
+ Assigner une clé maître
+ Créer un fichier keepass
- 30 secondes
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index 8a015a0be..0f82ce90a 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -45,7 +45,7 @@
Jelszó megerősítése:
Létrehozva:
Lejárat:
- Kulcsfájl (opcionális)
+ Kulcsfájl
Módosítva:
Jelszó:
Mentés
@@ -64,7 +64,7 @@
Érvénytelen adatbázis.
Érvénytelen útvonal.
Egy névre van szükség.
- Jelszóra vagy kulcsfájlra van szükség.
+ Jelszóra vagy kulcsfájlra van szükség.
A telefon memóriája megtelt az adatbázis feldolgozása közben. Lehet túl sok ez a telefonnak.
Legalább egy jelszógenerálási típust kell választania
A jelszavak nem egyeznek meg.
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 1d8ffae12..90431b61f 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -61,7 +61,7 @@
Conferma password:
Creato:
Scade:
- File chiave (opzionale)
+ File chiave
Modificato:
Password
Salva
@@ -80,7 +80,7 @@
Database non valido.
Percorso non valido.
E\' necessario un nome.
- E\' necessaria una password o un file chiave.
+ E\' necessaria una password o un file chiave.
Il telefono ha esaurito la memoria durante l\'elaborazione del database. Potrebbe essere troppo grande per il tuo telefono.
Almeno un tipo di generazione password deve essere selezionato
Le password non corrispondono.
diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml
index 2c09170c3..d4b0ddaf5 100644
--- a/app/src/main/res/values-iw/strings.xml
+++ b/app/src/main/res/values-iw/strings.xml
@@ -62,7 +62,7 @@
אשר סיסמה:
תאריך יצירה:
פג תוקף:
- קובץ מפתח (רשות)
+ קובץ מפתח
נערך לאחרונה:
נתוני ערך לא נמצאו.
סיסמה:
@@ -81,7 +81,7 @@
מסד נתונים לא חוקי.
נתיב לא חוקי.
שם נדרש.
- סיסמה או קובץ מפתח נדרשים.
+ סיסמה או קובץ מפתח נדרשים.
זיכרון המכשיר אזל בזמן ניתוח מסד הנתונים. יתכן והוא גדול מדי למכשירך.
לפחות סוג אחד ליצירת סיסמה צריך להיבחר
הסיסמאות לא תואמות.
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index f7e0ccc80..2b3d75066 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -58,7 +58,7 @@
パスワードの確認:
作成日:
有効期限:
- キーファイル(オプション)
+ キーファイル
更新日:
パスワード:
保存
@@ -77,7 +77,7 @@
無効なデータベースです。
パスが無効です。
タイトルは必須入力です。
- パスワードかキーファイルが必要です。
+ パスワードかキーファイルが必要です。
データベース解析中にメモリ不足になりました。
少なくとも1つ以上のパスワード生成タイプを選択する必要があります。
パスワードが一致しません
diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml
index dc358e943..167dd3a66 100644
--- a/app/src/main/res/values-lt/strings.xml
+++ b/app/src/main/res/values-lt/strings.xml
@@ -102,7 +102,7 @@
Įrašo duomenys nerasti.
Rakto failas yra tuščias.
Rakto failas neegzistuoja.
- Rakto failas (pasirinktinis)
+ Rakto failas
Įrašo pavadinimas/aprašymas
Pakeisti master raktą
Naudota:
diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml
index 71cfd6fdb..ef0d38155 100644
--- a/app/src/main/res/values-lv/strings.xml
+++ b/app/src/main/res/values-lv/strings.xml
@@ -45,7 +45,7 @@
Apstipriniet paroli:
Izveidots:
Derīguma termiņš beidzas:
- Atslēgas fails (pēc izvēles)
+ Atslēgas fails
Modificēts:
Parole:
Saglabāt
@@ -64,7 +64,7 @@
Nederīga datu bāze.
Nederīgs ceļš.
Vajag ievadīt faila nosaukumu
- Vajadzīga parole vai atslēgas fails.
+ Vajadzīga parole vai atslēgas fails.
Darbam ar datu bāzi, tālrunī nepietiek atmiņas.
Ir jāatlasa vismaz viens paroles ģenerēšanas tips
Paroles nesakrīt.
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 3ffcc311b..43d0a49fa 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -60,7 +60,7 @@
Bevestig wachtwoord:
Aangemaakt op:
Verloopt op:
- Sleutelbestand (optioneel)
+ Sleutelbestand
Gewijzigd op:
Wachtwoord:
Opslaan
@@ -79,7 +79,7 @@
Ongeldige database.
Ongeldige padnaam.
Een naam ontbreekt.
- Een wachtwoord of sleutenbestand ontbreekt.
+ Een wachtwoord of sleutenbestand ontbreekt.
Onvoldoende vrij geheugen om de database te lezen. Misschien is de database te groot voor deze telefoon.
U moet minstens één type van wachtwoordgeneratie kiezen.
Wachtwoorden zijn niet gelijk.
diff --git a/app/src/main/res/values-nn/strings.xml b/app/src/main/res/values-nn/strings.xml
index e9aef446f..7d75b7359 100644
--- a/app/src/main/res/values-nn/strings.xml
+++ b/app/src/main/res/values-nn/strings.xml
@@ -58,7 +58,7 @@
Stadfest passordet:
Laga:
Går ut:
- Nøkkelfil (valfri)
+ Nøkkelfil
Endra:
Passord:
Lagra
@@ -77,7 +77,7 @@
Ugyldig database.
Ugyldig stig.
Treng eit namn.
- Treng eit passord eller ei nøkkelfil.
+ Treng eit passord eller ei nøkkelfil.
Telefonen gjekk tom for minne ved lesinga av databasen din. Databasen er kanskje for stor.
Du må velja minst éin passordlagingstype
Passorda samsvarer ikkje.
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 755c0efff..42c83f8f1 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -56,7 +56,7 @@ along with KeePass DX. If not, see .
Potwierdź hasło:
Utworzono:
Wygasa:
- Plik klucza (opcjonalnie)
+ Plik klucza
Zmodyfikowano:
Hasło
Zapisz
@@ -75,7 +75,7 @@ along with KeePass DX. If not, see .
Nieprawidłowa baza danych.
Nieprawidłowa ścieżka dostępu.
Wymagana nazwa.
- Wymagane hasło lub plik klucza.
+ Wymagane hasło lub plik klucza.
Podczas parsowania bazy danych zabrakło pamięci. Być może ta baza danych jest za duża dla Twojego urządzenia.
Przynajmniej jeden sposób generowania hasła musi być wybrany
Hasła nie pasują do siebie.
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 399a22002..aa5fa7899 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -61,7 +61,7 @@
Confirmar senha:
Criado:
Expira:
- Arquivo de chave (opcional)
+ Arquivo de chave
Modificado:
Senha:
Salvar
@@ -80,7 +80,7 @@
Banco de dados inválido.
Caminho inválido.
É necessário um nome.
- São necessários uma senha ou um arquivo de chaves.
+ São necessários uma senha ou um arquivo de chaves.
Foi atingida a capacidade máxima de memória do seu dispositivo ao carregar o banco de dados. O banco de dados pode ser muito grande para este dispositivo.
Pelo menos um tipo de geração de senhas deve ser selecionado
As senhas não combinam.
diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml
index cce44c5cc..7bcffeb85 100644
--- a/app/src/main/res/values-pt-rPT/strings.xml
+++ b/app/src/main/res/values-pt-rPT/strings.xml
@@ -66,7 +66,7 @@
Confirmar palavra-passe:
Criado:
Expira:
- Ficheiro chave (opcional)
+ Ficheiro chave
Modificado:
Dados da entrada não encontrados.
Palavra-passe:
@@ -86,7 +86,7 @@
Base de dados inválida.
Caminho inválido.
É obrigatório introduzir um nome.
- É obrigatório introduzir uma palavra-passe ou um ficheiro chave.
+ É obrigatório introduzir uma palavra-passe ou um ficheiro chave.
O dispositivo ficou sem memória ao analisar a base de dados. Poderá ser muito grande para o seu dispositivo.
Pelo menos um tipo de geração de palavra-passe deve ser selecionado
As palavra-passe não coincidem.
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index ab478b88f..7d76c6173 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -86,7 +86,7 @@
Неверный формат базы
Путь указан неверно
Необходимо ввести имя файла
- Необходим пароль или файл-ключ
+ Необходим пароль или файл-ключ
Недостаточно памяти для работы с базой
Выберите один или несколько типов символов
Пароли не совпадают
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 1bbda85ff..1b936dab7 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -58,7 +58,7 @@
Potvrdiť heslo:
Vytvorené:
Expirácia:
- Súbor Keyfile (voliteľné)
+ Súbor Keyfile
Upravené:
Heslo:
Uložiť
@@ -77,7 +77,7 @@
Chybná databáza.
Chybná cesta.
Vyžaduje sa meno.
- Vyžaduje sa heslo, alebo súbor keyfile.
+ Vyžaduje sa heslo, alebo súbor keyfile.
Telefón vyčerpal pamäť pri analýze databázy. Možno je to príliš na Váš telefón.
Musí byť vybraý najmenej jeden typ generovania hesla
Heslá sa nezhodujú.
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index 73e746839..d3d2b72dd 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -65,7 +65,7 @@
Bekräfta lösenord:
Skapad:
Upphör att gälla:
- Nyckelfil (valfri)
+ Nyckelfil
Senast ändrad:
Lösenord:
Spara
@@ -84,7 +84,7 @@
Ogiltig databas.
Ogiltig sökväg.
Ett namn krävs.
- Ett lösenord eller en nyckelfil är ett krav.
+ Ett lösenord eller en nyckelfil är ett krav.
The phone ran out of memory while parsing your database. It may be too large for your phone.
At least one password generation type must be selected
Lösenorden matchar inte.
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 0ab49420b..284bafa5a 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -58,7 +58,7 @@
Підтвердження паролю:
Створено:
Закінчується:
- Файл ключа (необов’язково)
+ Файл ключа
Змінено:
Пароль:
Зберегти
@@ -77,7 +77,7 @@
Невірна база даних.
Невірний шлях.
Потрібне ім’я.
- Необхідно пароль або файл ключа.
+ Необхідно пароль або файл ключа.
Телефону не вистачило пам’яті при аналізі вашої бази даних. Можливо база надто велика для вашог телефона.
Принаймні один тип генерації пароля необхідно вибрати.
Паролі не співпадають.
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 9313adc45..a0c378754 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -58,7 +58,7 @@
确认密码:
创建:
失效时间:
- 密钥文件(可选)
+ 密钥文件
修改时间::
密码:
保存
@@ -77,7 +77,7 @@
非法数据库。
非法路径。
用户名是必须的。
- 密码或者密钥文件是必须的。
+ 密码或者密钥文件是必须的。
这款手机运行内存不足而不能解析数据库。这可能是数据库太大的缘故。
至少必须选择一个密码生成类型
密码不匹配。
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index b8aaca938..2840ffaaf 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -58,7 +58,7 @@
確認密碼:
創建:
失效時間:
- 密鑰文件(可選)
+ 密鑰文件
修改時間::
密碼:
保存
@@ -77,7 +77,7 @@
非法資料庫。
非法路徑。
用戶名是必須的。
- 密碼或者密鑰檔是必須的。
+ 密碼或者密鑰檔是必須的。
這款手機運行記憶體不足而不能解析資料庫。這可能是資料庫太大的緣故。
至少必須選擇一個密碼生成類型
密碼不匹配。
diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml
index 47bb8659c..519af70b9 100644
--- a/app/src/main/res/values/donottranslate.xml
+++ b/app/src/main/res/values/donottranslate.xml
@@ -21,7 +21,6 @@
KeePass DX
http://code.google.com/p/android/issues/detail?id=35732
- /keepass/keepass.kdbx
http://www.kunzisoft.com/KeepassDX
https://github.com/Kunzisoft/KeePassDX/issues
@@ -136,4 +135,12 @@
III
IV
V
+
+ /keepass/
+ keepass
+ .kdbx
+
+ - @string/database_file_extension_default
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 05f2d3321..08f23dc44 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -65,7 +65,7 @@
Confirm password:
Created:
Expires:
- Key file (optional)
+ Key file
Modified:
Entry data not found.
Password:
@@ -85,7 +85,7 @@
Invalid database.
Invalid path.
A name is required.
- A password or a keyfile is required.
+ A keyfile is required.
The phone ran out of memory while parsing your database. It may be too large for your phone.
At least one password generation type must be selected
Passwords do not match.
@@ -191,6 +191,8 @@
The .kdb format only supports the Latin1 character set. Your password may contain characters outside of this character set. All non-Latin1 charaters are converted to the same character, which reduces the security of your password. Changing your password is recommended.
Your sd card is currently read-only. You may not be able to save changes to your database.
Your sd card is not currently mounted on your device. You will not be able to load or create your database.
+ Do you really want to use an empty string as password ?
+ Are you sure you do not want to use any encryption key ?
Version:
Fingerprint supported but not configured for device
Listening for fingerprints
@@ -222,6 +224,10 @@
Fingerprint listening
Enable database opening by fingerprint
Can not start this feature\nYour Android version %1$d is not the minimum version %2$d required
+ File name
+ Path
+ Assign a master key
+ Create a keepass file
- 30 seconds
@@ -241,4 +247,5 @@
- Light Theme
- Night Theme
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 3fdf4a536..844787118 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -237,4 +237,34 @@
- 16sp
+
+
+
+
+
\ No newline at end of file
diff --git a/art/screen0.jpg b/art/screen0.jpg
index a42d54f3c..c27318a41 100644
Binary files a/art/screen0.jpg and b/art/screen0.jpg differ
diff --git a/art/screen1.jpg b/art/screen1.jpg
index d32507466..08849d0ca 100644
Binary files a/art/screen1.jpg and b/art/screen1.jpg differ