From fb1b90a281440ff813d654097ffd107b3fa4ac1f Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 21 Apr 2018 14:20:43 +0200 Subject: [PATCH] Add modification --- .../keepass/tests/database/DeleteEntry.java | 2 +- .../keepass/activities/EntryEditActivity.java | 2 +- .../keepass/activities/GroupActivity.java | 80 ++++++++----- .../keepass/activities/ListNodesActivity.java | 25 +++- .../keepass/adapters/NodeAdapter.java | 55 +++------ .../kunzisoft/keepass/database/Database.java | 46 +++++++ .../keepass/database/PwDatabaseV3.java | 16 +-- .../kunzisoft/keepass/database/PwEntry.java | 29 +---- .../kunzisoft/keepass/database/PwEntryV3.java | 5 +- .../kunzisoft/keepass/database/PwEntryV4.java | 4 +- .../kunzisoft/keepass/database/PwGroup.java | 12 +- .../kunzisoft/keepass/database/PwGroupV3.java | 20 ++++ .../kunzisoft/keepass/database/PwGroupV4.java | 48 +++++++- .../kunzisoft/keepass/database/PwNode.java | 24 ++-- .../keepass/database/edit/AddEntry.java | 10 +- .../keepass/database/edit/AddGroup.java | 35 ++---- ...nish.java => AfterActionNodeOnFinish.java} | 6 +- .../keepass/database/edit/DeleteGroup.java | 5 - .../keepass/database/edit/UpdateEntry.java | 16 ++- .../keepass/database/edit/UpdateGroup.java | 88 ++++++++++++++ .../dialogs/GroupEditDialogFragment.java | 113 +++++++++++++----- 21 files changed, 443 insertions(+), 198 deletions(-) rename app/src/main/java/com/kunzisoft/keepass/database/edit/{AfterAddNodeOnFinish.java => AfterActionNodeOnFinish.java} (83%) create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/edit/UpdateGroup.java diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java index 99f615a2a..b2748636a 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java @@ -60,7 +60,7 @@ public class DeleteEntry extends AndroidTestCase { assertNotNull("Could not find group1", group1); // Delete the group - DeleteGroup task = new DeleteGroup(db, group1, null, true); + DeleteGroup task = new DeleteGroup(null, db, group1, null, true); task.run(); // Verify the entries were deleted diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index fd274b0e1..a7d52f3e5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -175,7 +175,7 @@ public class EntryEditActivity extends LockingHideActivity if ( uuidBytes == null ) { PwGroupId parentId = (PwGroupId) intent.getSerializableExtra(KEY_PARENT); PwGroup parent = pm.getGroupByGroupId(parentId); - mEntry = PwEntry.getInstance(parent); + mEntry = db.createEntry(parent); mIsNew = true; // Add the default icon if (IconPackChooser.getSelectedIconPack(this).tintable()) { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 4fed4817e..14e3891f0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -53,11 +53,13 @@ import com.kunzisoft.keepass.database.Database; import com.kunzisoft.keepass.database.PwEntry; import com.kunzisoft.keepass.database.PwGroup; import com.kunzisoft.keepass.database.PwGroupId; +import com.kunzisoft.keepass.database.PwIconStandard; import com.kunzisoft.keepass.database.PwNode; import com.kunzisoft.keepass.database.SortNodeEnum; import com.kunzisoft.keepass.database.edit.AddGroup; import com.kunzisoft.keepass.database.edit.DeleteEntry; import com.kunzisoft.keepass.database.edit.DeleteGroup; +import com.kunzisoft.keepass.database.edit.UpdateGroup; import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment; import com.kunzisoft.keepass.dialogs.GroupEditDialogFragment; import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment; @@ -82,13 +84,10 @@ public class GroupActivity extends ListNodesActivity protected boolean addEntryEnabled = false; protected boolean isRoot = false; protected boolean readOnly = false; - protected EditGroupDialogAction editGroupDialogAction = EditGroupDialogAction.NONE; - - private enum EditGroupDialogAction { - CREATION, UPDATE, NONE - } private static final String TAG = "Group Activity:"; + + private PwGroup oldGroupToUpdate; public static void launch(Activity act) { recordFirstTimeBeforeLaunch(act); @@ -160,10 +159,9 @@ public class GroupActivity extends ListNodesActivity toolbar.setNavigationIcon(R.drawable.ic_arrow_up_white_24dp); addNodeButtonView.setAddGroupClickListener(v -> { - editGroupDialogAction = EditGroupDialogAction.CREATION; - GroupEditDialogFragment groupEditDialogFragment = new GroupEditDialogFragment(); - groupEditDialogFragment.show(getSupportFragmentManager(), - GroupEditDialogFragment.TAG_CREATE_GROUP); + GroupEditDialogFragment.build() + .show(getSupportFragmentManager(), + GroupEditDialogFragment.TAG_CREATE_GROUP); }); addNodeButtonView.setAddEntryClickListener(v -> EntryEditActivity.launch(GroupActivity.this, mCurrentGroup)); @@ -215,7 +213,6 @@ public class GroupActivity extends ListNodesActivity nodeAdapter.setNodeMenuListener(new NodeAdapter.NodeMenuListener() { @Override public boolean onOpenMenuClick(PwNode node) { - mAdapter.registerANodeToUpdate(node); switch (node.getType()) { case GROUP: GroupActivity.launch(GroupActivity.this, (PwGroup) node); @@ -229,14 +226,12 @@ public class GroupActivity extends ListNodesActivity @Override public boolean onEditMenuClick(PwNode node) { - mAdapter.registerANodeToUpdate(node); switch (node.getType()) { case GROUP: - editGroupDialogAction = EditGroupDialogAction.UPDATE; - GroupEditDialogFragment groupEditDialogFragment = - GroupEditDialogFragment.build(node); - groupEditDialogFragment.show(getSupportFragmentManager(), - GroupEditDialogFragment.TAG_CREATE_GROUP); + oldGroupToUpdate = (PwGroup) node; + GroupEditDialogFragment.build(node) + .show(getSupportFragmentManager(), + GroupEditDialogFragment.TAG_CREATE_GROUP); break; case ENTRY: EntryEditActivity.launch(GroupActivity.this, (PwEntry) node); @@ -528,28 +523,53 @@ public class GroupActivity extends ListNodesActivity } @Override - public void approveEditGroup(Bundle bundle) { - String GroupName = bundle.getString(GroupEditDialogFragment.KEY_NAME); - int GroupIconID = bundle.getInt(GroupEditDialogFragment.KEY_ICON_ID); - switch (editGroupDialogAction) { + public void approveEditGroup(GroupEditDialogFragment.EditGroupDialogAction action, + String name, + int iconId) { + Database database = App.getDB(); // TODO encapsulate iconFactory + PwIconStandard icon = database.getPwDatabase().getIconFactory().getIcon(iconId); + + switch (action) { case CREATION: - // If edit group creation - Handler handler = new Handler(); - AddGroup task = new AddGroup(this, App.getDB(), GroupName, GroupIconID, mCurrentGroup, - new AfterAddNode(handler), false); - ProgressTask pt = new ProgressTask(this, task, R.string.saving_database); - pt.run(); + // If group creation + // Build the group + PwGroup newGroup = database.createGroup(mCurrentGroup); + newGroup.setName(name); + newGroup.setIcon(icon); + + new ProgressTask(this, + new AddGroup(this, + App.getDB(), + newGroup, + new AfterAddNode(new Handler())), + R.string.saving_database) + .run(); break; case UPDATE: - // If edit group update - // TODO UpdateGroup + // If update add new elements + PwGroup updateGroup = oldGroupToUpdate.clone(); + updateGroup.setName(name); + updateGroup.setIcon(icon); + + mAdapter.removeNode(oldGroupToUpdate); + // If group update + new ProgressTask(this, + new UpdateGroup(this, + App.getDB(), + oldGroupToUpdate, + updateGroup, + new AfterUpdateNode(new Handler())), + R.string.saving_database) + .run(); + break; } - editGroupDialogAction = EditGroupDialogAction.NONE; } @Override - public void cancelEditGroup(Bundle bundle) { + public void cancelEditGroup(GroupEditDialogFragment.EditGroupDialogAction action, + String name, + int iconId) { // Do nothing here } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesActivity.java index e7d31de98..01b25f8ef 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesActivity.java @@ -49,7 +49,7 @@ import com.kunzisoft.keepass.database.PwEntry; import com.kunzisoft.keepass.database.PwGroup; import com.kunzisoft.keepass.database.PwNode; import com.kunzisoft.keepass.database.SortNodeEnum; -import com.kunzisoft.keepass.database.edit.AfterAddNodeOnFinish; +import com.kunzisoft.keepass.database.edit.AfterActionNodeOnFinish; import com.kunzisoft.keepass.database.edit.OnFinish; import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment; import com.kunzisoft.keepass.dialogs.SortDialogFragment; @@ -143,8 +143,6 @@ public abstract class ListNodesActivity extends LockingActivity @Override public void onNodeClick(PwNode node) { - mAdapter.registerANodeToUpdate(node); - // Add event when we have Autofill AssistStructure assistStructure = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -302,15 +300,30 @@ public abstract class ListNodesActivity extends LockingActivity } } - class AfterAddNode extends AfterAddNodeOnFinish { + class AfterAddNode extends AfterActionNodeOnFinish { AfterAddNode(Handler handler) { super(handler); } - public void run(PwNode pwNode) { + public void run(PwNode oldNode, PwNode newNode) { super.run(); if (mSuccess) { - mAdapter.addNode(pwNode); + mAdapter.addNode(newNode); + } else { + displayMessage(ListNodesActivity.this); + } + } + } + + class AfterUpdateNode extends AfterActionNodeOnFinish { + AfterUpdateNode(Handler handler) { + super(handler); + } + + public void run(PwNode oldNode, PwNode newNode) { + super.run(); + if (mSuccess) { + mAdapter.updateNode(oldNode, newNode); } else { displayMessage(ListNodesActivity.this); } diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java index 78abd3539..5c9e02c72 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java @@ -26,7 +26,6 @@ import android.support.annotation.NonNull; import android.support.v7.util.SortedList; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.util.SortedListAdapterCallback; -import android.util.Log; import android.view.ContextMenu; import android.view.LayoutInflater; import android.view.Menu; @@ -54,7 +53,6 @@ public class NodeAdapter extends RecyclerView.Adapter { private boolean ascendingSort; private OnNodeClickCallback onNodeClickCallback; - private int nodePositionToUpdate; private NodeMenuListener nodeMenuListener; private boolean activateContextMenu; @@ -73,7 +71,6 @@ public class NodeAdapter extends RecyclerView.Adapter { this.groupsBeforeSort = PreferencesUtil.getGroupsBeforeSort(context); this.ascendingSort = PreferencesUtil.getAscendingSort(context); this.activateContextMenu = false; - this.nodePositionToUpdate = -1; this.nodeSortedList = new SortedList<>(PwNode.class, new SortedListAdapterCallback(this) { @Override public int compare(PwNode item1, PwNode item2) { @@ -119,43 +116,25 @@ public class NodeAdapter extends RecyclerView.Adapter { } /** - * Register a node to update before an action - * Call updateLastNodeRegister() after the action to update the node - * @param node Node to register - */ - public void registerANodeToUpdate(PwNode node) { - nodePositionToUpdate = nodeSortedList.indexOf(node); - } - - /** - * Update the last Node register in the list - * Work if only registerANodeToUpdate(PwNode node) is called before - */ - public void updateLastNodeRegister(PwNode node) { - // Don't really update here, sorted list knows each original ref, so we just notify a change - try { - if (nodePositionToUpdate != -1) { - // Don't know why but there is a bug to remove a node after this update - nodeSortedList.updateItemAt(nodePositionToUpdate, node); - nodeSortedList.recalculatePositionOfItemAt(nodePositionToUpdate); - nodePositionToUpdate = -1; - } - else { - Log.e(NodeAdapter.class.getName(), "registerANodeToUpdate must be called before updateLastNodeRegister"); - } - } catch (IndexOutOfBoundsException e) { - Log.e(NodeAdapter.class.getName(), e.getMessage()); - } - } - - /** - * Remove node in the list + * Remove a node in the list * @param node Node to delete */ public void removeNode(PwNode node) { nodeSortedList.remove(node); } + /** + * Update a node in the list + * @param oldNode Node before the update + * @param newNode Node after the update + */ + public void updateNode(PwNode oldNode, PwNode newNode) { + nodeSortedList.beginBatchedUpdates(); + nodeSortedList.remove(oldNode); + nodeSortedList.add(newNode); + nodeSortedList.endBatchedUpdates(); + } + /** * Notify a change sort of the list */ @@ -170,6 +149,7 @@ public class NodeAdapter extends RecyclerView.Adapter { return nodeSortedList.get(position).getType().ordinal(); } + @NonNull @Override public BasicViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { BasicViewHolder basicViewHolder; @@ -290,9 +270,10 @@ public class NodeAdapter extends RecyclerView.Adapter { MenuItem clearMenu = contextMenu.add(Menu.NONE, MENU_OPEN, Menu.NONE, R.string.menu_open); clearMenu.setOnMenuItemClickListener(mOnMyActionClickListener); if (!App.getDB().isReadOnly() && !node.equals(App.getDB().getPwDatabase().getRecycleBin())) { - // TODO make edit for group - // clearMenu = contextMenu.add(Menu.NONE, MENU_EDIT, Menu.NONE, R.string.menu_edit); - // clearMenu.setOnMenuItemClickListener(mOnMyActionClickListener); + // Edition + clearMenu = contextMenu.add(Menu.NONE, MENU_EDIT, Menu.NONE, R.string.menu_edit); + clearMenu.setOnMenuItemClickListener(mOnMyActionClickListener); + // Deletion clearMenu = contextMenu.add(Menu.NONE, MENU_DELETE, Menu.NONE, R.string.menu_delete); clearMenu.setOnMenuItemClickListener(mOnMyActionClickListener); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/Database.java b/app/src/main/java/com/kunzisoft/keepass/database/Database.java index 0c088c3e0..433a973c4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/Database.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/Database.java @@ -368,6 +368,37 @@ public class Database { getPwDatabase().setNumberKeyEncryptionRounds(numberRounds); } + public PwEntry createEntry(PwGroup parent) { + PwEntry newPwEntry = null; + try { + switch (getPwDatabase().getVersion()) { + case V3: + newPwEntry = new PwEntryV3((PwGroupV3) parent); + case V4: + newPwEntry = new PwEntryV4((PwGroupV4) parent); + } + } catch (Exception e) { + Log.e(TAG, "This version of PwEntry can't be created", e); + } + return newPwEntry; + } + + public PwGroup createGroup(PwGroup parent) { + PwGroup newPwGroup = null; + try { + switch (getPwDatabase().getVersion()) { + case V3: + newPwGroup = new PwGroupV3((PwGroupV3) parent); + case V4: + newPwGroup = new PwGroupV4((PwGroupV4) parent); + } + newPwGroup.setId(pm.newGroupId()); + } catch (Exception e) { + Log.e(TAG, "This version of PwGroup can't be created", e); + } + return newPwGroup; + } + public void addEntryTo(PwEntry entry, PwGroup parent) { try { switch (getPwDatabase().getVersion()) { @@ -501,6 +532,21 @@ public class Database { } } + public void updateGroup(PwGroup oldGroup, PwGroup newGroup) { + try { + switch (getPwDatabase().getVersion()) { + case V3: + ((PwGroupV3) oldGroup).updateWith((PwGroupV3) newGroup); + break; + case V4: + ((PwGroupV4) oldGroup).updateWith((PwGroupV4) newGroup); + break; + } + } catch (Exception e) { + Log.e(TAG, "This version of PwEntry can't be updated", e); + } + } + public void deleteEntry(PwEntry entry) { try { switch (getPwDatabase().getVersion()) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwDatabaseV3.java b/app/src/main/java/com/kunzisoft/keepass/database/PwDatabaseV3.java index 9448aae0f..e8b55dbbc 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwDatabaseV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/PwDatabaseV3.java @@ -45,7 +45,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA package com.kunzisoft.keepass.database; -// Java import com.kunzisoft.keepass.crypto.keyDerivation.AesKdf; import com.kunzisoft.keepass.database.exception.InvalidKeyFileException; @@ -71,13 +70,6 @@ public class PwDatabaseV3 extends PwDatabase { private int numKeyEncRounds; - private void initAndAddGroup(String name, int iconId, PwGroupV3 parent) { - PwGroupV3 group = createGroup(); - group.initNewGroup(name, newGroupId()); - group.setIcon(iconFactory.getIcon(iconId)); - addGroupTo(group, parent); - } - @Override public void initNew(String dbPath) { algorithm = PwEncryptionAlgorithm.AES_Rijndael; @@ -90,6 +82,14 @@ public class PwDatabaseV3 extends PwDatabase { initAndAddGroup("eMail", 19, rootGroup); } + private void initAndAddGroup(String name, int iconId, PwGroupV3 parent) { + PwGroupV3 group = createGroup(); + group.setId(newGroupId()); + group.setName(name); + group.setIcon(iconFactory.getIcon(iconId)); + addGroupTo(group, parent); + } + @Override public PwVersion getVersion() { return PwVersion.V3; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwEntry.java b/app/src/main/java/com/kunzisoft/keepass/database/PwEntry.java index ebe31f3d8..ede7b0da1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwEntry.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/PwEntry.java @@ -24,7 +24,7 @@ import com.kunzisoft.keepass.database.security.ProtectedString; import java.util.UUID; -public abstract class PwEntry extends PwNode implements Cloneable { +public abstract class PwEntry extends PwNode { private static final String PMS_TAN_ENTRY = ""; @@ -36,35 +36,12 @@ public abstract class PwEntry extends PwNode imp uuid = UUID.randomUUID(); } - public static PwEntry getInstance(PwGroup parent) { - if (parent instanceof PwGroupV3) { - return new PwEntryV3((PwGroupV3)parent); - } - else if (parent instanceof PwGroupV4) { - return new PwEntryV4((PwGroupV4)parent); - } - else { - throw new RuntimeException("Unknown PwGroup instance."); - } - } - @Override public PwEntry clone() { - PwEntry newEntry; - try { - newEntry = (PwEntry) super.clone(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException("Clone should be supported"); - } - return newEntry; + // uuid is clone automatically (IMMUTABLE) + return (PwEntry) super.clone(); } - @Override - protected void addCloneAttributesToNewEntry(PwEntry newEntry) { - super.addCloneAttributesToNewEntry(newEntry); - // uuid is clone automatically (IMMUTABLE) - } - @Override public Type getType() { return Type.ENTRY; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwEntryV3.java b/app/src/main/java/com/kunzisoft/keepass/database/PwEntryV3.java index e370659ef..4afcc4b11 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwEntryV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/PwEntryV3.java @@ -98,6 +98,7 @@ public class PwEntryV3 extends PwEntry { } protected void updateWith(PwEntryV3 source) { + super.assign(source); groupId = source.groupId; title = source.title; @@ -118,10 +119,8 @@ public class PwEntryV3 extends PwEntry { @Override public PwEntryV3 clone() { - PwEntryV3 newEntry = (PwEntryV3) super.clone(); - // Attributes in parent - addCloneAttributesToNewEntry(newEntry); + PwEntryV3 newEntry = (PwEntryV3) super.clone(); // Attributes here // newEntry.parent stay the same in copy diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwEntryV4.java b/app/src/main/java/com/kunzisoft/keepass/database/PwEntryV4.java index 211e006e2..0c9df8767 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwEntryV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/PwEntryV4.java @@ -89,11 +89,9 @@ public class PwEntryV4 extends PwEntry implements ITimeLogger { @SuppressWarnings("unchecked") @Override public PwEntryV4 clone() { + // Attributes in parent PwEntryV4 newEntry = (PwEntryV4) super.clone(); - // Attributes in parent - addCloneAttributesToNewEntry(newEntry); - // Attributes here newEntry.customIcon = new PwIconCustom(this.customIcon); // newEntry.usageCount stay the same in copy diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwGroup.java b/app/src/main/java/com/kunzisoft/keepass/database/PwGroup.java index d8cdec744..46731a015 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwGroup.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/PwGroup.java @@ -30,9 +30,15 @@ public abstract class PwGroup childGroups = new ArrayList<>(); protected List childEntries = new ArrayList<>(); - public void initNewGroup(String nm, PwGroupId newId) { - setId(newId); - name = nm; + @Override + public PwGroup clone() { + // name is clone automatically (IMMUTABLE) + return (PwGroup) super.clone(); + } + + protected void assign(PwGroup source) { + super.assign(source); + name = source.name; } public List getChildGroups() { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwGroupV3.java b/app/src/main/java/com/kunzisoft/keepass/database/PwGroupV3.java index 0b94ce4a6..520bb5f46 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwGroupV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/PwGroupV3.java @@ -40,6 +40,26 @@ public class PwGroupV3 extends PwGroup { super(); } + public PwGroupV3(PwGroupV3 p) { + construct(p); + } + + protected void updateWith(PwGroupV3 source) { + super.assign(source); + groupId = source.groupId; + level = source.level; + flags = source.flags; + } + + @SuppressWarnings("unchecked") + @Override + public PwGroupV3 clone() { + // newGroup.groupId stay the same in copy + // newGroup.level stay the same in copy + // newGroup.flags stay the same in copy + return (PwGroupV3) super.clone(); + } + @Override public void setParent(PwGroupV3 parent) { super.setParent(parent); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwGroupV4.java b/app/src/main/java/com/kunzisoft/keepass/database/PwGroupV4.java index 4c3a0a449..f339970f5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwGroupV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/PwGroupV4.java @@ -45,6 +45,11 @@ public class PwGroupV4 extends PwGroup implemen public PwGroupV4() { super(); } + + public PwGroupV4(PwGroupV4 p) { + construct(p); + parentGroupLastMod = new PwDate(); + } public PwGroupV4(String name, PwIconStandard icon) { this.uuid = UUID.randomUUID(); @@ -52,10 +57,47 @@ public class PwGroupV4 extends PwGroup implemen this.icon = icon; } + protected void updateWith(PwGroupV4 source) { + super.assign(source); + uuid = source.uuid; + customIcon = source.customIcon; + usageCount = source.usageCount; + parentGroupLastMod = source.parentGroupLastMod; + customData = source.customData; + + expires = source.expires; + + notes = source.notes; + isExpanded = source.isExpanded; + defaultAutoTypeSequence = source.defaultAutoTypeSequence; + enableAutoType = source.enableAutoType; + enableSearching = source.enableSearching; + lastTopVisibleEntry = source.lastTopVisibleEntry; + } + + @SuppressWarnings("unchecked") @Override - public void initNewGroup(String nm, PwGroupId newId) { - super.initNewGroup(nm, newId); - parentGroupLastMod = new PwDate(); + public PwGroupV4 clone() { + // Attributes in parent + PwGroupV4 newGroup = (PwGroupV4) super.clone(); + + // Attributes here + // newGroup.uuid stay the same in copy + newGroup.customIcon = new PwIconCustom(this.customIcon); + // newGroup.usageCount stay the same in copy + newGroup.parentGroupLastMod = this.parentGroupLastMod.clone(); + // TODO customData make copy from hashmap newGroup.customData = (HashMap) this.customData.clone(); + + // newGroup.expires stay the same in copy + + // newGroup.notes stay the same in copy + // newGroup.isExpanded stay the same in copy + // newGroup.defaultAutoTypeSequence stay the same in copy + // newGroup.enableAutoType stay the same in copy + // newGroup.enableSearching stay the same in copy + // newGroup.lastTopVisibleEntry stay the same in copy + + return newGroup; } public void addGroup(PwGroupV4 subGroup) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwNode.java b/app/src/main/java/com/kunzisoft/keepass/database/PwNode.java index 9d332014c..69512069d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwNode.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/PwNode.java @@ -27,7 +27,7 @@ import java.io.Serializable; /** * Abstract class who manage Groups and Entries */ -public abstract class PwNode implements ISmallTimeLogger, Serializable { +public abstract class PwNode implements ISmallTimeLogger, Serializable, Cloneable { protected Parent parent = null; @@ -53,15 +53,23 @@ public abstract class PwNode implements ISmallTimeLogger this.expireDate = source.expireDate; } - protected void addCloneAttributesToNewEntry(PwEntry newEntry) { - // newEntry.parent stay the same in copy + @Override + public PwNode clone() { + PwNode newNode; + try { + newNode = (PwNode) super.clone(); + // newNode.parent stay the same in copy - newEntry.icon = new PwIconStandard(this.icon); + newNode.icon = new PwIconStandard(this.icon); - newEntry.creation = creation.clone(); - newEntry.lastMod = lastMod.clone(); - newEntry.lastAccess = lastAccess.clone(); - newEntry.expireDate = expireDate.clone(); + newNode.creation = creation.clone(); + newNode.lastMod = lastMod.clone(); + newNode.lastAccess = lastAccess.clone(); + newNode.expireDate = expireDate.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException("Clone should be supported"); + } + return newNode; } /** diff --git a/app/src/main/java/com/kunzisoft/keepass/database/edit/AddEntry.java b/app/src/main/java/com/kunzisoft/keepass/database/edit/AddEntry.java index 353cc42e4..4bc3b74b7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/edit/AddEntry.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/edit/AddEntry.java @@ -29,13 +29,19 @@ public class AddEntry extends RunnableOnFinish { protected Database mDb; private PwEntry mEntry; private Context ctx; - + private boolean mDontSave; + public AddEntry(Context ctx, Database db, PwEntry entry, OnFinish finish) { + this(ctx, db, entry, finish, false); + } + + public AddEntry(Context ctx, Database db, PwEntry entry, OnFinish finish, boolean dontSave) { super(finish); this.mDb = db; this.mEntry = entry; this.ctx = ctx; + this.mDontSave = dontSave; this.mFinish = new AfterAdd(mFinish); } @@ -45,7 +51,7 @@ public class AddEntry extends RunnableOnFinish { mDb.addEntryTo(mEntry, mEntry.getParent()); // Commit to disk - SaveDB save = new SaveDB(ctx, mDb, mFinish); + SaveDB save = new SaveDB(ctx, mDb, mFinish, mDontSave); save.run(); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/edit/AddGroup.java b/app/src/main/java/com/kunzisoft/keepass/database/edit/AddGroup.java index e43c5d35b..21691613a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/edit/AddGroup.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/edit/AddGroup.java @@ -22,28 +22,25 @@ package com.kunzisoft.keepass.database.edit; import android.content.Context; import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwDatabase; import com.kunzisoft.keepass.database.PwGroup; public class AddGroup extends RunnableOnFinish { protected Database mDb; - private String mName; - private int mIconID; - private PwGroup mGroup; - private PwGroup mParent; + private PwGroup mNewGroup; private Context ctx; private boolean mDontSave; - - public AddGroup(Context ctx, Database db, String name, int iconid, - PwGroup parent, AfterAddNodeOnFinish afterAddNode, + + public AddGroup(Context ctx, Database db, PwGroup newGroup, AfterActionNodeOnFinish afterAddNode) { + this(ctx, db, newGroup, afterAddNode, false); + } + + public AddGroup(Context ctx, Database db, PwGroup newGroup, AfterActionNodeOnFinish afterAddNode, boolean dontSave) { super(afterAddNode); this.mDb = db; - this.mName = name; - this.mIconID = iconid; - this.mParent = parent; + this.mNewGroup = newGroup; this.mDontSave = dontSave; this.ctx = ctx; @@ -52,13 +49,7 @@ public class AddGroup extends RunnableOnFinish { @Override public void run() { - PwDatabase pm = mDb.getPwDatabase(); - - // Generate new group - mGroup = pm.createGroup(); - mGroup.initNewGroup(mName, pm.newGroupId()); - mGroup.setIcon(pm.getIconFactory().getIcon(mIconID)); - mDb.addGroupTo(mGroup, mParent); + mDb.addGroupTo(mNewGroup, mNewGroup.getParent()); // Commit to disk SaveDB save = new SaveDB(ctx, mDb, mFinish, mDontSave); @@ -74,15 +65,15 @@ public class AddGroup extends RunnableOnFinish { @Override public void run() { if ( !mSuccess ) { - mDb.removeGroupFrom(mGroup, mParent); + mDb.removeGroupFrom(mNewGroup, mNewGroup.getParent()); } // TODO Better callback - AfterAddNodeOnFinish afterAddNode = - (AfterAddNodeOnFinish) super.mOnFinish; + AfterActionNodeOnFinish afterAddNode = + (AfterActionNodeOnFinish) super.mOnFinish; afterAddNode.mSuccess = mSuccess; afterAddNode.mMessage = mMessage; - afterAddNode.run(mGroup); + afterAddNode.run(null, mNewGroup); } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/edit/AfterAddNodeOnFinish.java b/app/src/main/java/com/kunzisoft/keepass/database/edit/AfterActionNodeOnFinish.java similarity index 83% rename from app/src/main/java/com/kunzisoft/keepass/database/edit/AfterAddNodeOnFinish.java rename to app/src/main/java/com/kunzisoft/keepass/database/edit/AfterActionNodeOnFinish.java index cb31d7807..6e4eb7493 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/edit/AfterAddNodeOnFinish.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/edit/AfterActionNodeOnFinish.java @@ -23,10 +23,10 @@ import android.os.Handler; import com.kunzisoft.keepass.database.PwNode; -public abstract class AfterAddNodeOnFinish extends OnFinish { - public AfterAddNodeOnFinish(Handler handler) { +public abstract class AfterActionNodeOnFinish extends OnFinish { + public AfterActionNodeOnFinish(Handler handler) { super(handler); } - public abstract void run(PwNode pwNode); + public abstract void run(PwNode oldNode, PwNode newNode); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/edit/DeleteGroup.java b/app/src/main/java/com/kunzisoft/keepass/database/edit/DeleteGroup.java index 552d8691e..6fc74c6d5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/edit/DeleteGroup.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/edit/DeleteGroup.java @@ -45,11 +45,6 @@ public class DeleteGroup extends RunnableOnFinish { super(finish); setMembers(ctx, db, group, dontSave); } - - public DeleteGroup(Database db, PwGroup group, OnFinish finish, boolean dontSave) { - super(finish); - setMembers(null, db, group, dontSave); - } private void setMembers(Context ctx, Database db, PwGroup group, boolean dontSave) { mDb = db; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/edit/UpdateEntry.java b/app/src/main/java/com/kunzisoft/keepass/database/edit/UpdateEntry.java index aef5a4170..75525f184 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/edit/UpdateEntry.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/edit/UpdateEntry.java @@ -30,14 +30,20 @@ public class UpdateEntry extends RunnableOnFinish { private PwEntry mOldE; private PwEntry mNewE; private Context ctx; - + private boolean mDontSave; + public UpdateEntry(Context ctx, Database db, PwEntry oldE, PwEntry newE, OnFinish finish) { + this(ctx, db, oldE, newE, finish, false); + } + + public UpdateEntry(Context ctx, Database db, PwEntry oldE, PwEntry newE, OnFinish finish, boolean dontSave) { super(finish); - mDb = db; - mOldE = oldE; - mNewE = newE; + this.mDb = db; + this.mOldE = oldE; + this.mNewE = newE; this.ctx = ctx; + this.mDontSave = dontSave; // Keep backup of original values in case save fails PwEntry backup; @@ -53,7 +59,7 @@ public class UpdateEntry extends RunnableOnFinish { mOldE.touch(true, true); // Commit to disk - SaveDB save = new SaveDB(ctx, mDb, mFinish); + SaveDB save = new SaveDB(ctx, mDb, mFinish, mDontSave); save.run(); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/edit/UpdateGroup.java b/app/src/main/java/com/kunzisoft/keepass/database/edit/UpdateGroup.java new file mode 100644 index 000000000..c9fdcf70e --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/edit/UpdateGroup.java @@ -0,0 +1,88 @@ +/* + * 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 . + * + */ +package com.kunzisoft.keepass.database.edit; + +import android.content.Context; + +import com.kunzisoft.keepass.database.Database; +import com.kunzisoft.keepass.database.PwGroup; + +public class UpdateGroup extends RunnableOnFinish { + + private Database mDb; + private PwGroup mOldGroup; + private PwGroup mNewGroup; + private Context ctx; + private boolean mDontSave; + + public UpdateGroup(Context ctx, Database db, PwGroup oldGroup, PwGroup newGroup, AfterActionNodeOnFinish finish) { + this(ctx, db, oldGroup, newGroup, finish, false); + } + + public UpdateGroup(Context ctx, Database db, PwGroup oldGroup, PwGroup newGroup, AfterActionNodeOnFinish finish, boolean dontSave) { + super(finish); + + this.mDb = db; + this.mOldGroup = oldGroup; + this.mNewGroup = newGroup; + this.ctx = ctx; + this.mDontSave = dontSave; + + // Keep backup of original values in case save fails + PwGroup backup; + backup = mOldGroup.clone(); + + this.mFinish = new AfterUpdate(backup, finish); + } + + @Override + public void run() { + // Update group with new values + mDb.updateGroup(mOldGroup, mNewGroup); + mOldGroup.touch(true, true); + + // Commit to disk + new SaveDB(ctx, mDb, mFinish, mDontSave).run(); + } + + private class AfterUpdate extends OnFinish { + private PwGroup mBackup; + + AfterUpdate(PwGroup backup, OnFinish finish) { + super(finish); + mBackup = backup; + } + + @Override + public void run() { + if ( !mSuccess ) { + // If we fail to save, back out changes to global structure + mDb.updateGroup(mOldGroup, mBackup); + } + + // TODO Better callback + AfterActionNodeOnFinish afterActionNodeOnFinish = + (AfterActionNodeOnFinish) super.mOnFinish; + afterActionNodeOnFinish.mSuccess = mSuccess; + afterActionNodeOnFinish.mMessage = mMessage; + afterActionNodeOnFinish.run(mOldGroup, mNewGroup); + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java b/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java index 20b22aff6..0f1ca6c57 100644 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java @@ -35,29 +35,50 @@ import android.widget.Toast; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.app.App; +import com.kunzisoft.keepass.database.PwIcon; import com.kunzisoft.keepass.database.PwNode; import com.kunzisoft.keepass.icons.IconPackChooser; +import static com.kunzisoft.keepass.dialogs.GroupEditDialogFragment.EditGroupDialogAction.CREATION; +import static com.kunzisoft.keepass.dialogs.GroupEditDialogFragment.EditGroupDialogAction.UPDATE; + public class GroupEditDialogFragment extends DialogFragment implements IconPickerDialogFragment.IconPickerListener { public static final String TAG_CREATE_GROUP = "TAG_CREATE_GROUP"; - public static final String KEY_NAME = "name"; - public static final String KEY_ICON_ID = "icon_id"; + public static final String KEY_NAME = "KEY_NAME"; + public static final String KEY_ICON_ID = "KEY_ICON_ID"; + public static final String KEY_ACTION_ID = "KEY_ACTION_ID"; + + private EditGroupDialogAction editGroupDialogAction = EditGroupDialogAction.NONE; private EditGroupListener editGroupListener; - private TextView nameField; private ImageView iconButton; private int mSelectedIconID; - private View root; + + public enum EditGroupDialogAction { + CREATION, UPDATE, NONE; + + public static EditGroupDialogAction getActionFromOrdinal(int ordinal) { + return EditGroupDialogAction.values()[ordinal]; + } + } + + public static GroupEditDialogFragment build() { + Bundle bundle = new Bundle(); + bundle.putInt(KEY_ACTION_ID, CREATION.ordinal()); + GroupEditDialogFragment fragment = new GroupEditDialogFragment(); + fragment.setArguments(bundle); + return fragment; + } public static GroupEditDialogFragment build(PwNode group) { Bundle bundle = new Bundle(); bundle.putString(KEY_NAME, group.getDisplayTitle()); - // TODO Change - bundle.putInt(KEY_ICON_ID, group.getIcon().hashCode()); + bundle.putSerializable(KEY_ICON_ID, group.getIcon()); + bundle.putInt(KEY_ACTION_ID, UPDATE.ordinal()); GroupEditDialogFragment fragment = new GroupEditDialogFragment(); fragment.setArguments(bundle); return fragment; @@ -80,27 +101,55 @@ public class GroupEditDialogFragment extends DialogFragment @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - + assert getActivity() != null; LayoutInflater inflater = getActivity().getLayoutInflater(); - root = inflater.inflate(R.layout.group_edit, null); - nameField = root.findViewById(R.id.group_name); + View root = inflater.inflate(R.layout.group_edit, null); + TextView nameField = root.findViewById(R.id.group_name); iconButton = root.findViewById(R.id.icon_button); + if (getArguments() != null + && getArguments().containsKey(KEY_ACTION_ID)) + editGroupDialogAction = EditGroupDialogAction.getActionFromOrdinal(getArguments().getInt(KEY_ACTION_ID)); + + // Retrieve the textColor to tint the icon + int[] attrs = {android.R.attr.textColorPrimary}; + TypedArray ta = getActivity().getTheme().obtainStyledAttributes(attrs); + int iconColor = ta.getColor(0, Color.WHITE); + if (getArguments() != null && getArguments().containsKey(KEY_NAME) && getArguments().containsKey(KEY_ICON_ID)) { nameField.setText(getArguments().getString(KEY_NAME)); - populateIcon(getArguments().getInt(KEY_ICON_ID)); - } else { - // populate the icon with the default one + // populate the icon if (IconPackChooser.getSelectedIconPack(getContext()).tintable()) { - // Retrieve the textColor to tint the icon - int[] attrs = {android.R.attr.textColorPrimary}; - TypedArray ta = getContext().getTheme().obtainStyledAttributes(attrs); - int iconColor = ta.getColor(0, Color.WHITE); - App.getDB().getDrawFactory().assignDefaultDatabaseIconTo(getContext(), iconButton, true, iconColor); + App.getDB().getDrawFactory() + .assignDatabaseIconTo( + getContext(), + iconButton, + (PwIcon) getArguments().getSerializable(KEY_ICON_ID), + true, + iconColor); } else { - App.getDB().getDrawFactory().assignDefaultDatabaseIconTo(getContext(), iconButton); + App.getDB().getDrawFactory() + .assignDatabaseIconTo( + getContext(), + iconButton, + (PwIcon) getArguments().getSerializable(KEY_ICON_ID)); + } + } else { + // populate the icon with the default one if not found + if (IconPackChooser.getSelectedIconPack(getContext()).tintable()) { + App.getDB().getDrawFactory() + .assignDefaultDatabaseIconTo( + getContext(), + iconButton, + true, + iconColor); + } else { + App.getDB().getDrawFactory() + .assignDefaultDatabaseIconTo( + getContext(), + iconButton); } } @@ -109,10 +158,10 @@ public class GroupEditDialogFragment extends DialogFragment .setPositiveButton(android.R.string.ok, (dialog, id) -> { String name = nameField.getText().toString(); if ( name.length() > 0 ) { - Bundle bundle = new Bundle(); - bundle.putString(KEY_NAME, name); - bundle.putInt(KEY_ICON_ID, mSelectedIconID); - editGroupListener.approveEditGroup(bundle); + editGroupListener.approveEditGroup( + editGroupDialogAction, + name, + mSelectedIconID); GroupEditDialogFragment.this.getDialog().cancel(); } @@ -121,32 +170,32 @@ public class GroupEditDialogFragment extends DialogFragment } }) .setNegativeButton(R.string.cancel, (dialog, id) -> { - Bundle bundle = new Bundle(); - editGroupListener.cancelEditGroup(bundle); + String name = nameField.getText().toString(); + editGroupListener.cancelEditGroup( + editGroupDialogAction, + name, + mSelectedIconID); GroupEditDialogFragment.this.getDialog().cancel(); }); iconButton.setOnClickListener(v -> { IconPickerDialogFragment iconPickerDialogFragment = new IconPickerDialogFragment(); - iconPickerDialogFragment.show(getFragmentManager(), "IconPickerDialogFragment"); + if (getFragmentManager() != null) + iconPickerDialogFragment.show(getFragmentManager(), "IconPickerDialogFragment"); }); return builder.create(); } - private void populateIcon(int iconId) { - iconButton.setImageResource(IconPackChooser.getSelectedIconPack(getContext()).iconToResId(iconId)); - } - @Override public void iconPicked(Bundle bundle) { mSelectedIconID = bundle.getInt(IconPickerDialogFragment.KEY_ICON_ID); - populateIcon(mSelectedIconID); + iconButton.setImageResource(IconPackChooser.getSelectedIconPack(getContext()).iconToResId(mSelectedIconID)); } public interface EditGroupListener { - void approveEditGroup(Bundle bundle); - void cancelEditGroup(Bundle bundle); + void approveEditGroup(EditGroupDialogAction action, String name, int selectedIconId); + void cancelEditGroup(EditGroupDialogAction action, String name, int selectedIconId); } }