mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Kotlinized EntryEditActivity
This commit is contained in:
@@ -1,567 +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 3 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package com.kunzisoft.keepass.activities;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.support.v7.widget.Toolbar;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.ScrollView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.kunzisoft.keepass.R;
|
|
||||||
import com.kunzisoft.keepass.activities.lock.LockingHideActivity;
|
|
||||||
import com.kunzisoft.keepass.app.App;
|
|
||||||
import com.kunzisoft.keepass.database.action.node.ActionNodeValues;
|
|
||||||
import com.kunzisoft.keepass.database.action.node.AddEntryRunnable;
|
|
||||||
import com.kunzisoft.keepass.database.action.node.AfterActionNodeFinishRunnable;
|
|
||||||
import com.kunzisoft.keepass.database.action.node.UpdateEntryRunnable;
|
|
||||||
import com.kunzisoft.keepass.database.element.Database;
|
|
||||||
import com.kunzisoft.keepass.database.element.EntryVersioned;
|
|
||||||
import com.kunzisoft.keepass.database.element.GroupVersioned;
|
|
||||||
import com.kunzisoft.keepass.database.element.PwDate;
|
|
||||||
import com.kunzisoft.keepass.database.element.PwIcon;
|
|
||||||
import com.kunzisoft.keepass.database.element.PwIconStandard;
|
|
||||||
import com.kunzisoft.keepass.database.element.PwNodeId;
|
|
||||||
import com.kunzisoft.keepass.database.element.security.ProtectedString;
|
|
||||||
import com.kunzisoft.keepass.dialogs.GeneratePasswordDialogFragment;
|
|
||||||
import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment;
|
|
||||||
import com.kunzisoft.keepass.education.EntryEditActivityEducation;
|
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil;
|
|
||||||
import com.kunzisoft.keepass.tasks.ActionRunnable;
|
|
||||||
import com.kunzisoft.keepass.timeout.TimeoutHelper;
|
|
||||||
import com.kunzisoft.keepass.utils.MenuUtil;
|
|
||||||
import com.kunzisoft.keepass.utils.Util;
|
|
||||||
import com.kunzisoft.keepass.view.EntryEditCustomField;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import static com.kunzisoft.keepass.dialogs.IconPickerDialogFragment.KEY_ICON_STANDARD;
|
|
||||||
|
|
||||||
public class EntryEditActivity extends LockingHideActivity
|
|
||||||
implements IconPickerDialogFragment.IconPickerListener,
|
|
||||||
GeneratePasswordDialogFragment.GeneratePasswordListener {
|
|
||||||
|
|
||||||
private static final String TAG = EntryEditActivity.class.getName();
|
|
||||||
|
|
||||||
// Keys for current Activity
|
|
||||||
public static final String KEY_ENTRY = "entry";
|
|
||||||
public static final String KEY_PARENT = "parent";
|
|
||||||
|
|
||||||
// Keys for callback
|
|
||||||
public static final int ADD_ENTRY_RESULT_CODE = 31;
|
|
||||||
public static final int UPDATE_ENTRY_RESULT_CODE = 32;
|
|
||||||
public static final int ADD_OR_UPDATE_ENTRY_REQUEST_CODE = 7129;
|
|
||||||
public static final String ADD_OR_UPDATE_ENTRY_KEY = "ADD_OR_UPDATE_ENTRY_KEY";
|
|
||||||
|
|
||||||
private Database database;
|
|
||||||
|
|
||||||
protected EntryVersioned mEntry;
|
|
||||||
protected GroupVersioned mParent;
|
|
||||||
protected EntryVersioned mNewEntry;
|
|
||||||
protected boolean mIsNew;
|
|
||||||
protected PwIconStandard mSelectedIconStandard;
|
|
||||||
|
|
||||||
// Views
|
|
||||||
private ScrollView scrollView;
|
|
||||||
private EditText entryTitleView;
|
|
||||||
private ImageView entryIconView;
|
|
||||||
private EditText entryUserNameView;
|
|
||||||
private EditText entryUrlView;
|
|
||||||
private EditText entryPasswordView;
|
|
||||||
private EditText entryConfirmationPasswordView;
|
|
||||||
private View generatePasswordView;
|
|
||||||
private EditText entryCommentView;
|
|
||||||
private ViewGroup entryExtraFieldsContainer;
|
|
||||||
private View addNewFieldView;
|
|
||||||
private View saveView;
|
|
||||||
private int iconColor;
|
|
||||||
|
|
||||||
// Education
|
|
||||||
private EntryEditActivityEducation entryEditActivityEducation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Launch EntryEditActivity to update an existing entry
|
|
||||||
*
|
|
||||||
* @param activity from activity
|
|
||||||
* @param pwEntry Entry to update
|
|
||||||
*/
|
|
||||||
public static void launch(Activity activity, EntryVersioned pwEntry) {
|
|
||||||
if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) {
|
|
||||||
Intent intent = new Intent(activity, EntryEditActivity.class);
|
|
||||||
intent.putExtra(KEY_ENTRY, pwEntry.getNodeId());
|
|
||||||
activity.startActivityForResult(intent, ADD_OR_UPDATE_ENTRY_REQUEST_CODE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Launch EntryEditActivity to add a new entry
|
|
||||||
*
|
|
||||||
* @param activity from activity
|
|
||||||
* @param pwGroup Group who will contains new entry
|
|
||||||
*/
|
|
||||||
public static void launch(Activity activity, GroupVersioned pwGroup) {
|
|
||||||
if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) {
|
|
||||||
Intent intent = new Intent(activity, EntryEditActivity.class);
|
|
||||||
intent.putExtra(KEY_PARENT, pwGroup.getNodeId());
|
|
||||||
activity.startActivityForResult(intent, ADD_OR_UPDATE_ENTRY_REQUEST_CODE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setContentView(R.layout.entry_edit);
|
|
||||||
|
|
||||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
|
||||||
setSupportActionBar(toolbar);
|
|
||||||
assert getSupportActionBar() != null;
|
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
|
||||||
|
|
||||||
scrollView = findViewById(R.id.entry_edit_scroll);
|
|
||||||
scrollView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
|
|
||||||
|
|
||||||
entryTitleView = findViewById(R.id.entry_edit_title);
|
|
||||||
entryIconView = findViewById(R.id.entry_edit_icon_button);
|
|
||||||
entryUserNameView = findViewById(R.id.entry_edit_user_name);
|
|
||||||
entryUrlView = findViewById(R.id.entry_edit_url);
|
|
||||||
entryPasswordView = findViewById(R.id.entry_edit_password);
|
|
||||||
entryConfirmationPasswordView = findViewById(R.id.entry_edit_confirmation_password);
|
|
||||||
entryCommentView = findViewById(R.id.entry_edit_notes);
|
|
||||||
entryExtraFieldsContainer = findViewById(R.id.entry_edit_advanced_container);
|
|
||||||
|
|
||||||
// Focus view to reinitialize timeout
|
|
||||||
resetAppTimeoutWhenViewFocusedOrChanged(
|
|
||||||
entryTitleView,
|
|
||||||
entryIconView,
|
|
||||||
entryUserNameView,
|
|
||||||
entryUrlView,
|
|
||||||
entryPasswordView,
|
|
||||||
entryConfirmationPasswordView,
|
|
||||||
entryCommentView,
|
|
||||||
entryExtraFieldsContainer);
|
|
||||||
|
|
||||||
// Likely the app has been killed exit the activity
|
|
||||||
database = App.Companion.getCurrentDatabase();
|
|
||||||
|
|
||||||
// Retrieve the textColor to tint the icon
|
|
||||||
int[] attrs = {android.R.attr.textColorPrimary};
|
|
||||||
TypedArray ta = getTheme().obtainStyledAttributes(attrs);
|
|
||||||
iconColor = ta.getColor(0, Color.WHITE);
|
|
||||||
|
|
||||||
mSelectedIconStandard = database.getIconFactory().getUnknownIcon();
|
|
||||||
|
|
||||||
Intent intent = getIntent();
|
|
||||||
// Entry is retrieve, it's an entry to update
|
|
||||||
PwNodeId keyEntry = intent.getParcelableExtra(KEY_ENTRY);
|
|
||||||
if (keyEntry != null) {
|
|
||||||
mIsNew = false;
|
|
||||||
mEntry = database.getEntryById(keyEntry);
|
|
||||||
if (mEntry != null) {
|
|
||||||
mParent = mEntry.getParent();
|
|
||||||
fillData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parent is retrieve, it's a new entry to create
|
|
||||||
PwNodeId keyParent = intent.getParcelableExtra(KEY_PARENT);
|
|
||||||
if (keyParent != null) {
|
|
||||||
mIsNew = true;
|
|
||||||
mEntry = database.createEntry();
|
|
||||||
mParent = database.getGroupById(keyParent);
|
|
||||||
// Add the default icon
|
|
||||||
database.getDrawFactory().assignDefaultDatabaseIconTo(this, entryIconView, iconColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close the activity if entry or parent can't be retrieve
|
|
||||||
if (mEntry == null || mParent == null) {
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assign title
|
|
||||||
setTitle((mIsNew) ? getString(R.string.add_entry) : getString(R.string.edit_entry));
|
|
||||||
|
|
||||||
// Retrieve the icon after an orientation change
|
|
||||||
if (savedInstanceState != null
|
|
||||||
&& savedInstanceState.containsKey(KEY_ICON_STANDARD)) {
|
|
||||||
iconPicked(savedInstanceState);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add listener to the icon
|
|
||||||
entryIconView.setOnClickListener(v ->
|
|
||||||
IconPickerDialogFragment.launch(EntryEditActivity.this));
|
|
||||||
|
|
||||||
// Generate password button
|
|
||||||
generatePasswordView = findViewById(R.id.entry_edit_generate_button);
|
|
||||||
generatePasswordView.setOnClickListener(v -> openPasswordGenerator());
|
|
||||||
|
|
||||||
// Save button
|
|
||||||
saveView = findViewById(R.id.entry_edit_save);
|
|
||||||
saveView.setOnClickListener(v -> saveEntry());
|
|
||||||
|
|
||||||
if (mEntry.allowExtraFields()) {
|
|
||||||
addNewFieldView = findViewById(R.id.entry_edit_add_new_field);
|
|
||||||
addNewFieldView.setVisibility(View.VISIBLE);
|
|
||||||
addNewFieldView.setOnClickListener(v -> addNewCustomField());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify the education views
|
|
||||||
entryEditActivityEducation = new EntryEditActivityEducation(this);
|
|
||||||
new Handler().post(() -> performedNextEducation(entryEditActivityEducation));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void performedNextEducation(EntryEditActivityEducation entryEditActivityEducation) {
|
|
||||||
if (entryEditActivityEducation.checkAndPerformedGeneratePasswordEducation(
|
|
||||||
generatePasswordView,
|
|
||||||
tapTargetView -> {
|
|
||||||
openPasswordGenerator();
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
tapTargetView -> {
|
|
||||||
performedNextEducation(entryEditActivityEducation);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
));
|
|
||||||
else if (mEntry.allowExtraFields()
|
|
||||||
&& !mEntry.containsCustomFields()
|
|
||||||
&& entryEditActivityEducation.checkAndPerformedEntryNewFieldEducation(
|
|
||||||
addNewFieldView,
|
|
||||||
tapTargetView -> {
|
|
||||||
addNewCustomField();
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
tapTargetView -> null)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open the password generator fragment
|
|
||||||
*/
|
|
||||||
private void openPasswordGenerator() {
|
|
||||||
GeneratePasswordDialogFragment generatePasswordDialogFragment = new GeneratePasswordDialogFragment();
|
|
||||||
generatePasswordDialogFragment.show(getSupportFragmentManager(), "PasswordGeneratorFragment");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a new view to fill in the information of the customized field
|
|
||||||
*/
|
|
||||||
private void addNewCustomField() {
|
|
||||||
EntryEditCustomField entryEditCustomField = new EntryEditCustomField(EntryEditActivity.this);
|
|
||||||
entryEditCustomField.setData("", new ProtectedString(false, ""));
|
|
||||||
boolean visibilityFontActivated = PreferencesUtil.fieldFontIsInVisibility(this);
|
|
||||||
entryEditCustomField.setFontVisibility(visibilityFontActivated);
|
|
||||||
entryExtraFieldsContainer.addView(entryEditCustomField);
|
|
||||||
|
|
||||||
// Scroll bottom
|
|
||||||
scrollView.post(() -> scrollView.fullScroll(ScrollView.FOCUS_DOWN));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the new entry or update an existing entry in the database
|
|
||||||
*/
|
|
||||||
private void saveEntry() {
|
|
||||||
if (!validateBeforeSaving()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Clone the entry
|
|
||||||
mNewEntry = new EntryVersioned(mEntry);
|
|
||||||
|
|
||||||
populateEntryWithViewInfo(mNewEntry);
|
|
||||||
|
|
||||||
// Open a progress dialog and save entry
|
|
||||||
ActionRunnable task;
|
|
||||||
AfterActionNodeFinishRunnable afterActionNodeFinishRunnable =
|
|
||||||
new AfterActionNodeFinishRunnable() {
|
|
||||||
@Override
|
|
||||||
public void onActionNodeFinish(@NotNull ActionNodeValues actionNodeValues) {
|
|
||||||
if (actionNodeValues.getSuccess())
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if ( mIsNew ) {
|
|
||||||
task = new AddEntryRunnable(EntryEditActivity.this,
|
|
||||||
database,
|
|
||||||
mNewEntry,
|
|
||||||
mParent,
|
|
||||||
afterActionNodeFinishRunnable,
|
|
||||||
!getReadOnly());
|
|
||||||
} else {
|
|
||||||
task = new UpdateEntryRunnable(EntryEditActivity.this,
|
|
||||||
database,
|
|
||||||
mEntry,
|
|
||||||
mNewEntry,
|
|
||||||
afterActionNodeFinishRunnable,
|
|
||||||
!getReadOnly());
|
|
||||||
}
|
|
||||||
new Thread(task).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility class to retrieve a validation or an error with a message
|
|
||||||
*/
|
|
||||||
private class ErrorValidation {
|
|
||||||
static final int unknownMessage = -1;
|
|
||||||
|
|
||||||
boolean isValidate = false;
|
|
||||||
int messageId = unknownMessage;
|
|
||||||
|
|
||||||
void showValidationErrorIfNeeded() {
|
|
||||||
if (!isValidate && messageId != unknownMessage)
|
|
||||||
Toast.makeText(EntryEditActivity.this, messageId, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate or not the entry form
|
|
||||||
*
|
|
||||||
* @return ErrorValidation An error with a message or a validation without message
|
|
||||||
*/
|
|
||||||
protected ErrorValidation validate() {
|
|
||||||
ErrorValidation errorValidation = new ErrorValidation();
|
|
||||||
|
|
||||||
// Require title
|
|
||||||
String title = entryTitleView.getText().toString();
|
|
||||||
if ( title.length() == 0 ) {
|
|
||||||
errorValidation.messageId = R.string.error_title_required;
|
|
||||||
return errorValidation;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate password
|
|
||||||
String pass = entryPasswordView.getText().toString();
|
|
||||||
String conf = entryConfirmationPasswordView.getText().toString();
|
|
||||||
if ( ! pass.equals(conf) ) {
|
|
||||||
errorValidation.messageId = R.string.error_pass_match;
|
|
||||||
return errorValidation;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate extra fields
|
|
||||||
if (mEntry.allowExtraFields()) {
|
|
||||||
for (int i = 0; i < entryExtraFieldsContainer.getChildCount(); i++) {
|
|
||||||
EntryEditCustomField entryEditCustomField = (EntryEditCustomField) entryExtraFieldsContainer.getChildAt(i);
|
|
||||||
String key = entryEditCustomField.getLabel();
|
|
||||||
if (key == null || key.length() == 0) {
|
|
||||||
errorValidation.messageId = R.string.error_string_key;
|
|
||||||
return errorValidation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
errorValidation.isValidate = true;
|
|
||||||
return errorValidation;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Launch a validation with {@link #validate()} and show the error if present
|
|
||||||
*
|
|
||||||
* @return true if the form was validate or false if not
|
|
||||||
*/
|
|
||||||
protected boolean validateBeforeSaving() {
|
|
||||||
ErrorValidation errorValidation = validate();
|
|
||||||
errorValidation.showValidationErrorIfNeeded();
|
|
||||||
return errorValidation.isValidate;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void populateEntryWithViewInfo(EntryVersioned newEntry) {
|
|
||||||
|
|
||||||
database.startManageEntry(newEntry);
|
|
||||||
|
|
||||||
newEntry.setLastAccessTime(new PwDate());
|
|
||||||
newEntry.setLastModificationTime(new PwDate());
|
|
||||||
|
|
||||||
newEntry.setTitle(entryTitleView.getText().toString());
|
|
||||||
newEntry.setIcon(retrieveIcon());
|
|
||||||
|
|
||||||
newEntry.setUrl(entryUrlView.getText().toString());
|
|
||||||
newEntry.setUsername(entryUserNameView.getText().toString());
|
|
||||||
newEntry.setNotes(entryCommentView.getText().toString());
|
|
||||||
newEntry.setPassword(entryPasswordView.getText().toString());
|
|
||||||
|
|
||||||
if (newEntry.allowExtraFields()) {
|
|
||||||
// Delete all extra strings
|
|
||||||
newEntry.removeAllCustomFields();
|
|
||||||
// Add extra fields from views
|
|
||||||
for (int i = 0; i < entryExtraFieldsContainer.getChildCount(); i++) {
|
|
||||||
EntryEditCustomField view = (EntryEditCustomField) entryExtraFieldsContainer.getChildAt(i);
|
|
||||||
String key = view.getLabel();
|
|
||||||
String value = view.getValue();
|
|
||||||
boolean protect = view.isProtected();
|
|
||||||
newEntry.addExtraField(key, new ProtectedString(protect, value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
database.stopManageEntry(newEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the icon by the selection, or the first icon in the list if the entry is new or the last one
|
|
||||||
*/
|
|
||||||
private PwIcon retrieveIcon() {
|
|
||||||
|
|
||||||
if (!mSelectedIconStandard.isUnknown())
|
|
||||||
return mSelectedIconStandard;
|
|
||||||
else {
|
|
||||||
if (mIsNew) {
|
|
||||||
return database.getIconFactory().getKeyIcon();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Keep previous icon, if no new one was selected
|
|
||||||
return mEntry.getIcon();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
super.onCreateOptionsMenu(menu);
|
|
||||||
|
|
||||||
MenuInflater inflater = getMenuInflater();
|
|
||||||
inflater.inflate(R.menu.database_lock, menu);
|
|
||||||
MenuUtil.INSTANCE.contributionMenuInflater(inflater, menu);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
switch ( item.getItemId() ) {
|
|
||||||
case R.id.menu_lock:
|
|
||||||
lockAndExit();
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case R.id.menu_contribute:
|
|
||||||
return MenuUtil.INSTANCE.onContributionItemSelected(this);
|
|
||||||
|
|
||||||
case android.R.id.home:
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assignIconView() {
|
|
||||||
database.getDrawFactory()
|
|
||||||
.assignDatabaseIconTo(
|
|
||||||
this,
|
|
||||||
entryIconView,
|
|
||||||
mEntry.getIcon(),
|
|
||||||
iconColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void fillData() {
|
|
||||||
|
|
||||||
assignIconView();
|
|
||||||
|
|
||||||
// Don't start the field reference manager, we want to see the raw ref
|
|
||||||
App.Companion.getCurrentDatabase().stopManageEntry(mEntry);
|
|
||||||
|
|
||||||
entryTitleView.setText(mEntry.getTitle());
|
|
||||||
entryUserNameView.setText(mEntry.getUsername());
|
|
||||||
entryUrlView.setText(mEntry.getUrl());
|
|
||||||
String password = mEntry.getPassword();
|
|
||||||
entryPasswordView.setText(password);
|
|
||||||
entryConfirmationPasswordView.setText(password);
|
|
||||||
entryCommentView.setText(mEntry.getNotes());
|
|
||||||
|
|
||||||
boolean visibilityFontActivated = PreferencesUtil.fieldFontIsInVisibility(this);
|
|
||||||
if (visibilityFontActivated) {
|
|
||||||
Util.applyFontVisibilityTo(this, entryUserNameView);
|
|
||||||
Util.applyFontVisibilityTo(this, entryPasswordView);
|
|
||||||
Util.applyFontVisibilityTo(this, entryConfirmationPasswordView);
|
|
||||||
Util.applyFontVisibilityTo(this, entryCommentView);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mEntry.allowExtraFields()) {
|
|
||||||
LinearLayout container = findViewById(R.id.entry_edit_advanced_container);
|
|
||||||
mEntry.getFields().doActionToAllCustomProtectedField((key, value) -> {
|
|
||||||
EntryEditCustomField entryEditCustomField = new EntryEditCustomField(EntryEditActivity.this);
|
|
||||||
entryEditCustomField.setData(key, value);
|
|
||||||
entryEditCustomField.setFontVisibility(visibilityFontActivated);
|
|
||||||
container.addView(entryEditCustomField);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void iconPicked(Bundle bundle) {
|
|
||||||
mSelectedIconStandard = bundle.getParcelable(KEY_ICON_STANDARD);
|
|
||||||
mEntry.setIcon(mSelectedIconStandard);
|
|
||||||
assignIconView();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onSaveInstanceState(Bundle outState) {
|
|
||||||
if (!mSelectedIconStandard.isUnknown()) {
|
|
||||||
outState.putParcelable(KEY_ICON_STANDARD, mSelectedIconStandard);
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void acceptPassword(Bundle bundle) {
|
|
||||||
String generatedPassword = bundle.getString(GeneratePasswordDialogFragment.KEY_PASSWORD_ID);
|
|
||||||
entryPasswordView.setText(generatedPassword);
|
|
||||||
entryConfirmationPasswordView.setText(generatedPassword);
|
|
||||||
|
|
||||||
new Handler().post(() -> performedNextEducation(entryEditActivityEducation));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cancelPassword(Bundle bundle) {
|
|
||||||
// Do nothing here
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void finish() {
|
|
||||||
// Assign entry callback as a result in all case
|
|
||||||
try {
|
|
||||||
if (mNewEntry != null) {
|
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
Intent intentEntry = new Intent();
|
|
||||||
bundle.putParcelable(ADD_OR_UPDATE_ENTRY_KEY, mNewEntry);
|
|
||||||
intentEntry.putExtras(bundle);
|
|
||||||
if (mIsNew) {
|
|
||||||
setResult(ADD_ENTRY_RESULT_CODE, intentEntry);
|
|
||||||
} else {
|
|
||||||
setResult(UPDATE_ENTRY_RESULT_CODE, intentEntry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.finish();
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Exception when parcelable can't be done
|
|
||||||
Log.e(TAG, "Cant add entry as result", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,542 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 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 3 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.kunzisoft.keepass.activities
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Handler
|
||||||
|
import android.support.v7.widget.Toolbar
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.ScrollView
|
||||||
|
import android.widget.Toast
|
||||||
|
|
||||||
|
import com.kunzisoft.keepass.R
|
||||||
|
import com.kunzisoft.keepass.activities.lock.LockingHideActivity
|
||||||
|
import com.kunzisoft.keepass.app.App
|
||||||
|
import com.kunzisoft.keepass.database.action.node.ActionNodeValues
|
||||||
|
import com.kunzisoft.keepass.database.action.node.AddEntryRunnable
|
||||||
|
import com.kunzisoft.keepass.database.action.node.AfterActionNodeFinishRunnable
|
||||||
|
import com.kunzisoft.keepass.database.action.node.UpdateEntryRunnable
|
||||||
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
|
import com.kunzisoft.keepass.database.element.EntryVersioned
|
||||||
|
import com.kunzisoft.keepass.database.element.GroupVersioned
|
||||||
|
import com.kunzisoft.keepass.database.element.PwDate
|
||||||
|
import com.kunzisoft.keepass.database.element.PwIcon
|
||||||
|
import com.kunzisoft.keepass.database.element.PwIconStandard
|
||||||
|
import com.kunzisoft.keepass.database.element.PwNodeId
|
||||||
|
import com.kunzisoft.keepass.database.element.security.ProtectedString
|
||||||
|
import com.kunzisoft.keepass.dialogs.GeneratePasswordDialogFragment
|
||||||
|
import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment
|
||||||
|
import com.kunzisoft.keepass.education.EntryEditActivityEducation
|
||||||
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
|
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||||
|
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||||
|
import com.kunzisoft.keepass.utils.MenuUtil
|
||||||
|
import com.kunzisoft.keepass.utils.Util
|
||||||
|
import com.kunzisoft.keepass.view.EntryEditCustomField
|
||||||
|
|
||||||
|
import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment.KEY_ICON_STANDARD
|
||||||
|
|
||||||
|
class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPickerListener, GeneratePasswordDialogFragment.GeneratePasswordListener {
|
||||||
|
|
||||||
|
private var mDatabase: Database? = null
|
||||||
|
|
||||||
|
private var mEntry: EntryVersioned? = null
|
||||||
|
private var mParent: GroupVersioned? = null
|
||||||
|
private var mNewEntry: EntryVersioned? = null
|
||||||
|
private var mIsNew: Boolean = false
|
||||||
|
private var mSelectedIconStandard: PwIconStandard? = null
|
||||||
|
|
||||||
|
// Views
|
||||||
|
private var scrollView: ScrollView? = null
|
||||||
|
private var entryTitleView: EditText? = null
|
||||||
|
private var entryIconView: ImageView? = null
|
||||||
|
private var entryUserNameView: EditText? = null
|
||||||
|
private var entryUrlView: EditText? = null
|
||||||
|
private var entryPasswordView: EditText? = null
|
||||||
|
private var entryConfirmationPasswordView: EditText? = null
|
||||||
|
private var generatePasswordView: View? = null
|
||||||
|
private var entryCommentView: EditText? = null
|
||||||
|
private var entryExtraFieldsContainer: ViewGroup? = null
|
||||||
|
private var addNewFieldView: View? = null
|
||||||
|
private var saveView: View? = null
|
||||||
|
private var iconColor: Int = 0
|
||||||
|
|
||||||
|
// View validation message
|
||||||
|
private var validationErrorMessageId = UNKNOWN_MESSAGE
|
||||||
|
|
||||||
|
// Education
|
||||||
|
private var entryEditActivityEducation: EntryEditActivityEducation? = null
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.entry_edit)
|
||||||
|
|
||||||
|
val toolbar = findViewById<Toolbar>(R.id.toolbar)
|
||||||
|
setSupportActionBar(toolbar)
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||||
|
|
||||||
|
scrollView = findViewById(R.id.entry_edit_scroll)
|
||||||
|
scrollView?.scrollBarStyle = View.SCROLLBARS_INSIDE_INSET
|
||||||
|
|
||||||
|
entryTitleView = findViewById(R.id.entry_edit_title)
|
||||||
|
entryIconView = findViewById(R.id.entry_edit_icon_button)
|
||||||
|
entryUserNameView = findViewById(R.id.entry_edit_user_name)
|
||||||
|
entryUrlView = findViewById(R.id.entry_edit_url)
|
||||||
|
entryPasswordView = findViewById(R.id.entry_edit_password)
|
||||||
|
entryConfirmationPasswordView = findViewById(R.id.entry_edit_confirmation_password)
|
||||||
|
entryCommentView = findViewById(R.id.entry_edit_notes)
|
||||||
|
entryExtraFieldsContainer = findViewById(R.id.entry_edit_advanced_container)
|
||||||
|
|
||||||
|
// Focus view to reinitialize timeout
|
||||||
|
resetAppTimeoutWhenViewFocusedOrChanged(
|
||||||
|
entryTitleView,
|
||||||
|
entryIconView,
|
||||||
|
entryUserNameView,
|
||||||
|
entryUrlView,
|
||||||
|
entryPasswordView,
|
||||||
|
entryConfirmationPasswordView,
|
||||||
|
entryCommentView,
|
||||||
|
entryExtraFieldsContainer)
|
||||||
|
|
||||||
|
// Likely the app has been killed exit the activity
|
||||||
|
mDatabase = App.currentDatabase
|
||||||
|
|
||||||
|
// Retrieve the textColor to tint the icon
|
||||||
|
iconColor = theme
|
||||||
|
.obtainStyledAttributes(intArrayOf(android.R.attr.textColorPrimary))
|
||||||
|
.getColor(0, Color.WHITE)
|
||||||
|
|
||||||
|
mSelectedIconStandard = mDatabase?.iconFactory?.unknownIcon
|
||||||
|
|
||||||
|
// Entry is retrieve, it's an entry to update
|
||||||
|
intent.getParcelableExtra<PwNodeId<*>>(KEY_ENTRY)?.let {
|
||||||
|
mIsNew = false
|
||||||
|
mEntry = mDatabase?.getEntryById(it)
|
||||||
|
mEntry?.let { entry ->
|
||||||
|
mParent = entry.parent
|
||||||
|
fillEntryDataInContentsView(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parent is retrieve, it's a new entry to create
|
||||||
|
intent.getParcelableExtra<PwNodeId<*>>(KEY_PARENT)?.let {
|
||||||
|
mIsNew = true
|
||||||
|
mEntry = mDatabase?.createEntry()
|
||||||
|
mParent = mDatabase?.getGroupById(it)
|
||||||
|
// Add the default icon
|
||||||
|
mDatabase?.drawFactory?.assignDefaultDatabaseIconTo(this, entryIconView, iconColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the activity if entry or parent can't be retrieve
|
||||||
|
if (mEntry == null || mParent == null) {
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign title
|
||||||
|
title = if (mIsNew) getString(R.string.add_entry) else getString(R.string.edit_entry)
|
||||||
|
|
||||||
|
// Retrieve the icon after an orientation change
|
||||||
|
savedInstanceState?.let {
|
||||||
|
if (it.containsKey(KEY_ICON_STANDARD)) {
|
||||||
|
iconPicked(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add listener to the icon
|
||||||
|
entryIconView?.setOnClickListener { IconPickerDialogFragment.launch(this@EntryEditActivity) }
|
||||||
|
|
||||||
|
// Generate password button
|
||||||
|
generatePasswordView = findViewById(R.id.entry_edit_generate_button)
|
||||||
|
generatePasswordView?.setOnClickListener { openPasswordGenerator() }
|
||||||
|
|
||||||
|
// Save button
|
||||||
|
saveView = findViewById(R.id.entry_edit_save)
|
||||||
|
mEntry?.let { entry ->
|
||||||
|
saveView?.setOnClickListener { saveEntry(entry) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mEntry?.allowExtraFields() == true) {
|
||||||
|
addNewFieldView = findViewById(R.id.entry_edit_add_new_field)
|
||||||
|
addNewFieldView?.apply {
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
setOnClickListener { addNewCustomField() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the education views
|
||||||
|
entryEditActivityEducation = EntryEditActivityEducation(this)
|
||||||
|
entryEditActivityEducation?.let {
|
||||||
|
Handler().post { performedNextEducation(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun performedNextEducation(entryEditActivityEducation: EntryEditActivityEducation) {
|
||||||
|
if (generatePasswordView != null
|
||||||
|
&& entryEditActivityEducation.checkAndPerformedGeneratePasswordEducation(
|
||||||
|
generatePasswordView!!,
|
||||||
|
{
|
||||||
|
openPasswordGenerator()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
performedNextEducation(entryEditActivityEducation)
|
||||||
|
}
|
||||||
|
))
|
||||||
|
else if (mEntry != null
|
||||||
|
&& mEntry!!.allowExtraFields()
|
||||||
|
&& !mEntry!!.containsCustomFields()
|
||||||
|
&& addNewFieldView != null
|
||||||
|
&& entryEditActivityEducation.checkAndPerformedEntryNewFieldEducation(
|
||||||
|
addNewFieldView!!,
|
||||||
|
{
|
||||||
|
addNewCustomField()
|
||||||
|
}))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the password generator fragment
|
||||||
|
*/
|
||||||
|
private fun openPasswordGenerator() {
|
||||||
|
GeneratePasswordDialogFragment().show(supportFragmentManager, "PasswordGeneratorFragment")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new view to fill in the information of the customized field
|
||||||
|
*/
|
||||||
|
private fun addNewCustomField() {
|
||||||
|
val entryEditCustomField = EntryEditCustomField(this@EntryEditActivity)
|
||||||
|
entryEditCustomField.setData("", ProtectedString(false, ""))
|
||||||
|
val visibilityFontActivated = PreferencesUtil.fieldFontIsInVisibility(this)
|
||||||
|
entryEditCustomField.setFontVisibility(visibilityFontActivated)
|
||||||
|
entryExtraFieldsContainer?.addView(entryEditCustomField)
|
||||||
|
|
||||||
|
// Scroll bottom
|
||||||
|
scrollView?.post { scrollView?.fullScroll(ScrollView.FOCUS_DOWN) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the new entry or update an existing entry in the database
|
||||||
|
*/
|
||||||
|
private fun saveEntry(entry: EntryVersioned) {
|
||||||
|
|
||||||
|
// Launch a validation and show the error if present
|
||||||
|
if (!isValid()) {
|
||||||
|
if (validationErrorMessageId != UNKNOWN_MESSAGE)
|
||||||
|
Toast.makeText(this@EntryEditActivity, validationErrorMessageId, Toast.LENGTH_LONG).show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Clone the entry
|
||||||
|
mDatabase?.let { database ->
|
||||||
|
mNewEntry = EntryVersioned(entry)
|
||||||
|
mNewEntry?.let { newEntry ->
|
||||||
|
populateEntryWithViewInfo(newEntry)
|
||||||
|
|
||||||
|
// Open a progress dialog and save entry
|
||||||
|
var task: ActionRunnable? = null
|
||||||
|
val afterActionNodeFinishRunnable = object : AfterActionNodeFinishRunnable() {
|
||||||
|
override fun onActionNodeFinish(actionNodeValues: ActionNodeValues) {
|
||||||
|
if (actionNodeValues.success)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mIsNew) {
|
||||||
|
mParent?.let { parent ->
|
||||||
|
task = AddEntryRunnable(this@EntryEditActivity,
|
||||||
|
database,
|
||||||
|
newEntry,
|
||||||
|
parent,
|
||||||
|
afterActionNodeFinishRunnable,
|
||||||
|
!readOnly)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
task = UpdateEntryRunnable(this@EntryEditActivity,
|
||||||
|
database,
|
||||||
|
entry,
|
||||||
|
newEntry,
|
||||||
|
afterActionNodeFinishRunnable,
|
||||||
|
!readOnly)
|
||||||
|
}
|
||||||
|
Thread(task).start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate or not the entry form
|
||||||
|
*
|
||||||
|
* @return ErrorValidation An error with a message or a validation without message
|
||||||
|
*/
|
||||||
|
private fun isValid(): Boolean {
|
||||||
|
|
||||||
|
// Require title
|
||||||
|
if (entryTitleView?.text.toString().isEmpty()) {
|
||||||
|
validationErrorMessageId = R.string.error_title_required
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate password
|
||||||
|
if (entryPasswordView?.text.toString() != entryConfirmationPasswordView?.text.toString()) {
|
||||||
|
validationErrorMessageId = R.string.error_pass_match
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate extra fields
|
||||||
|
if (mEntry?.allowExtraFields() == true) {
|
||||||
|
entryExtraFieldsContainer?.let {
|
||||||
|
for (i in 0 until it.childCount) {
|
||||||
|
val entryEditCustomField = it.getChildAt(i) as EntryEditCustomField
|
||||||
|
val key = entryEditCustomField.label
|
||||||
|
if (key == null || key.isEmpty()) {
|
||||||
|
validationErrorMessageId = R.string.error_string_key
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun populateEntryWithViewInfo(newEntry: EntryVersioned) {
|
||||||
|
|
||||||
|
mDatabase?.startManageEntry(newEntry)
|
||||||
|
|
||||||
|
newEntry.lastAccessTime = PwDate()
|
||||||
|
newEntry.lastModificationTime = PwDate()
|
||||||
|
|
||||||
|
newEntry.title = entryTitleView?.text.toString()
|
||||||
|
newEntry.icon = retrieveIcon()
|
||||||
|
|
||||||
|
newEntry.url = entryUrlView?.text.toString()
|
||||||
|
newEntry.username = entryUserNameView?.text.toString()
|
||||||
|
newEntry.notes = entryCommentView?.text.toString()
|
||||||
|
newEntry.password = entryPasswordView?.text.toString()
|
||||||
|
|
||||||
|
if (newEntry.allowExtraFields()) {
|
||||||
|
// Delete all extra strings
|
||||||
|
newEntry.removeAllCustomFields()
|
||||||
|
// Add extra fields from views
|
||||||
|
entryExtraFieldsContainer?.let {
|
||||||
|
for (i in 0 until it.childCount) {
|
||||||
|
val view = it.getChildAt(i) as EntryEditCustomField
|
||||||
|
val key = view.label
|
||||||
|
val value = view.value
|
||||||
|
val protect = view.isProtected
|
||||||
|
newEntry.addExtraField(key, ProtectedString(protect, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mDatabase?.stopManageEntry(newEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the icon by the selection, or the first icon in the list if the entry is new or the last one
|
||||||
|
*/
|
||||||
|
private fun retrieveIcon(): PwIcon {
|
||||||
|
|
||||||
|
return if (mSelectedIconStandard?.isUnknown != true)
|
||||||
|
mSelectedIconStandard
|
||||||
|
else {
|
||||||
|
if (mIsNew) {
|
||||||
|
mDatabase?.iconFactory?.keyIcon
|
||||||
|
} else {
|
||||||
|
// Keep previous icon, if no new one was selected
|
||||||
|
mEntry?.icon
|
||||||
|
}
|
||||||
|
} ?: PwIconStandard()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
|
super.onCreateOptionsMenu(menu)
|
||||||
|
|
||||||
|
val inflater = menuInflater
|
||||||
|
inflater.inflate(R.menu.database_lock, menu)
|
||||||
|
MenuUtil.contributionMenuInflater(inflater, menu)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
when (item.itemId) {
|
||||||
|
R.id.menu_lock -> {
|
||||||
|
lockAndExit()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.menu_contribute -> return MenuUtil.onContributionItemSelected(this)
|
||||||
|
|
||||||
|
android.R.id.home -> finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignIconView() {
|
||||||
|
mEntry?.icon?.let {
|
||||||
|
mDatabase?.drawFactory?.assignDatabaseIconTo(
|
||||||
|
this,
|
||||||
|
entryIconView,
|
||||||
|
it,
|
||||||
|
iconColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fillEntryDataInContentsView(entry: EntryVersioned) {
|
||||||
|
|
||||||
|
assignIconView()
|
||||||
|
|
||||||
|
// Don't start the field reference manager, we want to see the raw ref
|
||||||
|
mDatabase?.stopManageEntry(entry)
|
||||||
|
|
||||||
|
entryTitleView?.setText(entry.title)
|
||||||
|
entryUserNameView?.setText(entry.username)
|
||||||
|
entryUrlView?.setText(entry.url)
|
||||||
|
val password = entry.password
|
||||||
|
entryPasswordView?.setText(password)
|
||||||
|
entryConfirmationPasswordView?.setText(password)
|
||||||
|
entryCommentView?.setText(entry.notes)
|
||||||
|
|
||||||
|
val visibilityFontActivated = PreferencesUtil.fieldFontIsInVisibility(this)
|
||||||
|
if (visibilityFontActivated) {
|
||||||
|
Util.applyFontVisibilityTo(this, entryUserNameView)
|
||||||
|
Util.applyFontVisibilityTo(this, entryPasswordView)
|
||||||
|
Util.applyFontVisibilityTo(this, entryConfirmationPasswordView)
|
||||||
|
Util.applyFontVisibilityTo(this, entryCommentView)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.allowExtraFields()) {
|
||||||
|
val container = findViewById<LinearLayout>(R.id.entry_edit_advanced_container)
|
||||||
|
entry.fields.doActionToAllCustomProtectedField { key, value ->
|
||||||
|
val entryEditCustomField = EntryEditCustomField(this@EntryEditActivity)
|
||||||
|
entryEditCustomField.setData(key, value)
|
||||||
|
entryEditCustomField.setFontVisibility(visibilityFontActivated)
|
||||||
|
container.addView(entryEditCustomField)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun iconPicked(bundle: Bundle) {
|
||||||
|
mSelectedIconStandard = bundle.getParcelable(KEY_ICON_STANDARD)
|
||||||
|
mSelectedIconStandard?.let {
|
||||||
|
mEntry?.icon = it
|
||||||
|
}
|
||||||
|
assignIconView()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
if (!mSelectedIconStandard!!.isUnknown) {
|
||||||
|
outState.putParcelable(KEY_ICON_STANDARD, mSelectedIconStandard)
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun acceptPassword(bundle: Bundle) {
|
||||||
|
bundle.getString(GeneratePasswordDialogFragment.KEY_PASSWORD_ID)?.let {
|
||||||
|
entryPasswordView?.setText(it)
|
||||||
|
entryConfirmationPasswordView?.setText(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
entryEditActivityEducation?.let {
|
||||||
|
Handler().post { performedNextEducation(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun cancelPassword(bundle: Bundle) {
|
||||||
|
// Do nothing here
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finish() {
|
||||||
|
// Assign entry callback as a result in all case
|
||||||
|
try {
|
||||||
|
mNewEntry?.let {
|
||||||
|
val bundle = Bundle()
|
||||||
|
val intentEntry = Intent()
|
||||||
|
bundle.putParcelable(ADD_OR_UPDATE_ENTRY_KEY, mNewEntry)
|
||||||
|
intentEntry.putExtras(bundle)
|
||||||
|
if (mIsNew) {
|
||||||
|
setResult(ADD_ENTRY_RESULT_CODE, intentEntry)
|
||||||
|
} else {
|
||||||
|
setResult(UPDATE_ENTRY_RESULT_CODE, intentEntry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.finish()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// Exception when parcelable can't be done
|
||||||
|
Log.e(TAG, "Cant add entry as result", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private val TAG = EntryEditActivity::class.java.name
|
||||||
|
|
||||||
|
// Keys for current Activity
|
||||||
|
const val KEY_ENTRY = "entry"
|
||||||
|
const val KEY_PARENT = "parent"
|
||||||
|
|
||||||
|
// Keys for callback
|
||||||
|
const val ADD_ENTRY_RESULT_CODE = 31
|
||||||
|
const val UPDATE_ENTRY_RESULT_CODE = 32
|
||||||
|
const val ADD_OR_UPDATE_ENTRY_REQUEST_CODE = 7129
|
||||||
|
const val ADD_OR_UPDATE_ENTRY_KEY = "ADD_OR_UPDATE_ENTRY_KEY"
|
||||||
|
|
||||||
|
const val UNKNOWN_MESSAGE = -1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launch EntryEditActivity to update an existing entry
|
||||||
|
*
|
||||||
|
* @param activity from activity
|
||||||
|
* @param pwEntry Entry to update
|
||||||
|
*/
|
||||||
|
fun launch(activity: Activity, pwEntry: EntryVersioned) {
|
||||||
|
if (TimeoutHelper.checkTimeAndLockIfTimeout(activity)) {
|
||||||
|
val intent = Intent(activity, EntryEditActivity::class.java)
|
||||||
|
intent.putExtra(KEY_ENTRY, pwEntry.nodeId)
|
||||||
|
activity.startActivityForResult(intent, ADD_OR_UPDATE_ENTRY_REQUEST_CODE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launch EntryEditActivity to add a new entry
|
||||||
|
*
|
||||||
|
* @param activity from activity
|
||||||
|
* @param pwGroup Group who will contains new entry
|
||||||
|
*/
|
||||||
|
fun launch(activity: Activity, pwGroup: GroupVersioned) {
|
||||||
|
if (TimeoutHelper.checkTimeAndLockIfTimeout(activity)) {
|
||||||
|
val intent = Intent(activity, EntryEditActivity::class.java)
|
||||||
|
intent.putExtra(KEY_PARENT, pwGroup.nodeId)
|
||||||
|
activity.startActivityForResult(intent, ADD_OR_UPDATE_ENTRY_REQUEST_CODE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -298,7 +298,7 @@ public class GroupActivity extends LockingActivity
|
|||||||
.show(getSupportFragmentManager(),
|
.show(getSupportFragmentManager(),
|
||||||
GroupEditDialogFragment.TAG_CREATE_GROUP));
|
GroupEditDialogFragment.TAG_CREATE_GROUP));
|
||||||
addNodeButtonView.setAddEntryClickListener(v ->
|
addNodeButtonView.setAddEntryClickListener(v ->
|
||||||
EntryEditActivity.launch(GroupActivity.this, mCurrentGroup));
|
EntryEditActivity.Companion.launch(GroupActivity.this, mCurrentGroup));
|
||||||
|
|
||||||
// Search suggestion
|
// Search suggestion
|
||||||
searchSuggestionAdapter = new SearchEntryCursorAdapter(this, database);
|
searchSuggestionAdapter = new SearchEntryCursorAdapter(this, database);
|
||||||
@@ -570,7 +570,7 @@ public class GroupActivity extends LockingActivity
|
|||||||
GroupEditDialogFragment.TAG_CREATE_GROUP);
|
GroupEditDialogFragment.TAG_CREATE_GROUP);
|
||||||
break;
|
break;
|
||||||
case ENTRY:
|
case ENTRY:
|
||||||
EntryEditActivity.launch(GroupActivity.this, (EntryVersioned) node);
|
EntryEditActivity.Companion.launch(GroupActivity.this, (EntryVersioned) node);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -163,9 +163,9 @@ abstract class LockingActivity : StylishActivity() {
|
|||||||
/**
|
/**
|
||||||
* To reset the app timeout when a view is focused or changed
|
* To reset the app timeout when a view is focused or changed
|
||||||
*/
|
*/
|
||||||
protected fun resetAppTimeoutWhenViewFocusedOrChanged(vararg views: View) {
|
protected fun resetAppTimeoutWhenViewFocusedOrChanged(vararg views: View?) {
|
||||||
views.forEach {
|
views.forEach {
|
||||||
it.setOnFocusChangeListener { _, hasFocus ->
|
it?.setOnFocusChangeListener { _, hasFocus ->
|
||||||
if (hasFocus) {
|
if (hasFocus) {
|
||||||
TimeoutHelper.checkTimeAndLockIfTimeoutOrResetTimeout(this)
|
TimeoutHelper.checkTimeAndLockIfTimeoutOrResetTimeout(this)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user