Solve bugs when update node in list, start encapsulate code

This commit is contained in:
Jeremy
2018-02-12 00:22:03 +01:00
parent 795d6fa334
commit d568604117
18 changed files with 388 additions and 158 deletions

View File

@@ -74,8 +74,8 @@ public class DeleteEntry extends AndroidTestCase {
PwGroup results1 = dbHelp.search(db, ENTRY1_NAME); PwGroup results1 = dbHelp.search(db, ENTRY1_NAME);
PwGroup results2 = dbHelp.search(db, ENTRY2_NAME); PwGroup results2 = dbHelp.search(db, ENTRY2_NAME);
assertEquals("Entry1 was not removed from the search results", 0, results1.childEntries.size()); assertEquals("Entry1 was not removed from the search results", 0, results1.numbersOfChildEntries());
assertEquals("Entry2 was not removed from the search results", 0, results2.childEntries.size()); assertEquals("Entry2 was not removed from the search results", 0, results2.numbersOfChildEntries());
// Verify the group was deleted // Verify the group was deleted
group1 = getGroup(pm, GROUP1_NAME); group1 = getGroup(pm, GROUP1_NAME);

View File

@@ -43,7 +43,7 @@ public class SearchTest extends AndroidTestCase {
public void testSearch() { public void testSearch() {
PwGroup results = mDb.Search("Amazon"); PwGroup results = mDb.Search("Amazon");
assertTrue("Search result not found.", results.childEntries.size() > 0); assertTrue("Search result not found.", results.numbersOfChildEntries() > 0);
} }
@@ -51,14 +51,14 @@ public class SearchTest extends AndroidTestCase {
updateOmitSetting(false); updateOmitSetting(false);
PwGroup results = mDb.Search("BackupOnly"); PwGroup results = mDb.Search("BackupOnly");
assertTrue("Search result not found.", results.childEntries.size() > 0); assertTrue("Search result not found.", results.numbersOfChildEntries() > 0);
} }
public void testBackupExcluded() { public void testBackupExcluded() {
updateOmitSetting(true); updateOmitSetting(true);
PwGroup results = mDb.Search("BackupOnly"); PwGroup results = mDb.Search("BackupOnly");
assertFalse("Search result found, but should not have been.", results.childEntries.size() > 0); assertFalse("Search result found, but should not have been.", results.numbersOfChildEntries() > 0);
} }
private void updateOmitSetting(boolean setting) { private void updateOmitSetting(boolean setting) {

View File

@@ -34,13 +34,8 @@ import android.widget.ImageButton;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.keepassdroid.database.Database;
import com.keepassdroid.fragments.GeneratePasswordDialogFragment;
import com.keepassdroid.fragments.IconPickerDialogFragment;
import com.keepassdroid.tasks.ProgressTask;
import com.kunzisoft.keepass.KeePass;
import com.kunzisoft.keepass.R;
import com.keepassdroid.app.App; import com.keepassdroid.app.App;
import com.keepassdroid.database.Database;
import com.keepassdroid.database.PwDatabase; import com.keepassdroid.database.PwDatabase;
import com.keepassdroid.database.PwEntry; import com.keepassdroid.database.PwEntry;
import com.keepassdroid.database.PwEntryV3; import com.keepassdroid.database.PwEntryV3;
@@ -54,10 +49,15 @@ import com.keepassdroid.database.edit.AddEntry;
import com.keepassdroid.database.edit.OnFinish; import com.keepassdroid.database.edit.OnFinish;
import com.keepassdroid.database.edit.RunnableOnFinish; import com.keepassdroid.database.edit.RunnableOnFinish;
import com.keepassdroid.database.edit.UpdateEntry; import com.keepassdroid.database.edit.UpdateEntry;
import com.keepassdroid.fragments.GeneratePasswordDialogFragment;
import com.keepassdroid.fragments.IconPickerDialogFragment;
import com.keepassdroid.icons.Icons; import com.keepassdroid.icons.Icons;
import com.keepassdroid.tasks.ProgressTask;
import com.keepassdroid.utils.MenuUtil; import com.keepassdroid.utils.MenuUtil;
import com.keepassdroid.utils.Types; import com.keepassdroid.utils.Types;
import com.keepassdroid.utils.Util; import com.keepassdroid.utils.Util;
import com.kunzisoft.keepass.KeePass;
import com.kunzisoft.keepass.R;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
@@ -187,9 +187,9 @@ public abstract class EntryEditActivity extends LockCloseHideActivity
} }
PwEntry newEntry = populateNewEntry(); PwEntry newEntry = populateNewEntry();
RunnableOnFinish task; RunnableOnFinish task;
OnFinish onFinish = act.new AfterSave(new Handler()); OnFinish onFinish = new AfterSave();
Intent intentEntry = new Intent(); Intent intentEntry = new Intent();
if ( mIsNew ) { if ( mIsNew ) {
@@ -198,7 +198,7 @@ public abstract class EntryEditActivity extends LockCloseHideActivity
setResult(ADD_ENTRY_RESULT_CODE, intentEntry); setResult(ADD_ENTRY_RESULT_CODE, intentEntry);
} else { } else {
task = new UpdateEntry(act, App.getDB(), mEntry, newEntry, onFinish); task = new UpdateEntry(act, App.getDB(), mEntry, newEntry, onFinish);
intentEntry.putExtra(ADD_OR_UPDATE_ENTRY_KEY, mEntry); intentEntry.putExtra(ADD_OR_UPDATE_ENTRY_KEY, newEntry);
setResult(UPDATE_ENTRY_RESULT_CODE, intentEntry); setResult(UPDATE_ENTRY_RESULT_CODE, intentEntry);
} }
ProgressTask pt = new ProgressTask(act, task, R.string.saving_database); ProgressTask pt = new ProgressTask(act, task, R.string.saving_database);
@@ -322,8 +322,8 @@ public abstract class EntryEditActivity extends LockCloseHideActivity
private final class AfterSave extends OnFinish { private final class AfterSave extends OnFinish {
AfterSave(Handler handler) { AfterSave() {
super(handler); super(new Handler());
} }
@Override @Override

View File

@@ -251,7 +251,7 @@ public abstract class GroupActivity extends GroupBaseActivity
if (resultCode == EntryEditActivity.ADD_ENTRY_RESULT_CODE) if (resultCode == EntryEditActivity.ADD_ENTRY_RESULT_CODE)
mAdapter.addNode(newNode); mAdapter.addNode(newNode);
if (resultCode == EntryEditActivity.UPDATE_ENTRY_RESULT_CODE) if (resultCode == EntryEditActivity.UPDATE_ENTRY_RESULT_CODE)
mAdapter.updateNode(newNode); mAdapter.updateLastNodeClicked();
} }
break; break;
} }

View File

@@ -261,8 +261,8 @@ public abstract class GroupBaseActivity extends LockCloseListActivity
db.dirty.remove(mGroup); db.dirty.remove(mGroup);
// Tell the adapter to refresh it's list // Tell the adapter to refresh it's list
// TODO mAdapter.sort(); mAdapter.notifyChangeSort();
mAdapter.notifyDataSetChanged(); mAdapter.rebuildList(mGroup);
} }
@Override @Override

View File

@@ -1,3 +1,23 @@
/*
* Copyright 2018 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 <http://www.gnu.org/licenses/>.
*
*
*/
package com.keepassdroid.adapters; package com.keepassdroid.adapters;
import android.content.Context; import android.content.Context;
@@ -13,6 +33,7 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import com.keepassdroid.app.App; import com.keepassdroid.app.App;
import com.keepassdroid.database.PwGroup;
import com.keepassdroid.database.PwNode; import com.keepassdroid.database.PwNode;
import com.keepassdroid.settings.PrefsUtil; import com.keepassdroid.settings.PrefsUtil;
import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.R;
@@ -27,25 +48,30 @@ public class NodeAdapter extends RecyclerView.Adapter<BasicViewHolder> {
private Context context; private Context context;
private LayoutInflater inflater; private LayoutInflater inflater;
private float textSize; private float textSize;
private boolean sortByName;
private OnNodeClickCallback onNodeClickCallback; private OnNodeClickCallback onNodeClickCallback;
private int nodePositionToUpdate;
private NodeMenuListener nodeMenuListener; private NodeMenuListener nodeMenuListener;
public NodeAdapter(final Context context, PwNode mainNode) { public NodeAdapter(final Context context, PwGroup mainNode) {
this.inflater = LayoutInflater.from(context); this.inflater = LayoutInflater.from(context);
this.context = context; this.context = context;
this.textSize = PrefsUtil.getListTextSize(context); this.textSize = PrefsUtil.getListTextSize(context);
this.sortByName = PrefsUtil.isListSortByName(context);
this.nodePositionToUpdate = -1;
this.nodeSortedList = new SortedList<>(PwNode.class, new SortedListAdapterCallback<PwNode>(this) { this.nodeSortedList = new SortedList<>(PwNode.class, new SortedListAdapterCallback<PwNode>(this) {
@Override public int compare(PwNode item1, PwNode item2) { @Override public int compare(PwNode item1, PwNode item2) {
if(PrefsUtil.isListSortByName(context)) // Choose sort depend of preferences
return item1.compareTo(item2); if(sortByName)
return new PwNode.NodeNameComparator().compare(item1, item2);
else else
return item1.compareTo(item2); // TODO Different sort return new PwNode.NodeCreationComparator().compare(item1, item2);
} }
@Override public boolean areContentsTheSame(PwNode oldItem, PwNode newItem) { @Override public boolean areContentsTheSame(PwNode oldItem, PwNode newItem) {
return oldItem.equals(newItem); return oldItem.isContentVisuallyTheSame(newItem);
} }
@Override public boolean areItemsTheSame(PwNode item1, PwNode item2) { @Override public boolean areItemsTheSame(PwNode item1, PwNode item2) {
@@ -55,22 +81,51 @@ public class NodeAdapter extends RecyclerView.Adapter<BasicViewHolder> {
this.nodeSortedList.addAll(mainNode.getDirectChildren()); this.nodeSortedList.addAll(mainNode.getDirectChildren());
} }
/**
* Rebuild the list by clear and build again for the group
*/
public void rebuildList(PwGroup group) {
this.nodeSortedList.clear();
this.nodeSortedList.addAll(group.getDirectChildren());
}
/**
* Add a node to the list
* @param node Node to add
*/
public void addNode(PwNode node) { public void addNode(PwNode node) {
nodeSortedList.add(node); nodeSortedList.add(node);
} }
public void updateNode(PwNode node) { /**
* Update the last Node clicked in the list
*/
public void updateLastNodeClicked() {
// Don't really update here, sorted list knows each original ref, so we just notify a change
try { try {
nodeSortedList.updateItemAt(nodeSortedList.indexOf(node), node); notifyItemChanged(nodePositionToUpdate);
nodeSortedList.recalculatePositionOfItemAt(nodePositionToUpdate);
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
Log.e(NodeAdapter.class.getName(), e.getMessage()); Log.e(NodeAdapter.class.getName(), e.getMessage());
} }
} }
/**
* Remove node in the list
* @param node Node to delete
*/
public void removeNode(PwNode node) { public void removeNode(PwNode node) {
nodeSortedList.remove(node); nodeSortedList.remove(node);
} }
/**
* Notify a change sort of the list
*/
// TODO as interface
public void notifyChangeSort() {
this.sortByName = PrefsUtil.isListSortByName(context);
}
@Override @Override
public int getItemViewType(int position) { public int getItemViewType(int position) {
return nodeSortedList.get(position).getType().ordinal(); return nodeSortedList.get(position).getType().ordinal();
@@ -112,19 +167,39 @@ public class NodeAdapter extends RecyclerView.Adapter<BasicViewHolder> {
return nodeSortedList.size(); return nodeSortedList.size();
} }
/**
* Assign a listener when a node is clicked
*/
public void setOnNodeClickListener(OnNodeClickCallback onNodeClickCallback) { public void setOnNodeClickListener(OnNodeClickCallback onNodeClickCallback) {
this.onNodeClickCallback = onNodeClickCallback; this.onNodeClickCallback = onNodeClickCallback;
} }
/**
* Assign a listener when an element of menu is clicked
*/
public void setNodeMenuListener(NodeMenuListener nodeMenuListener) { public void setNodeMenuListener(NodeMenuListener nodeMenuListener) {
this.nodeMenuListener = nodeMenuListener; this.nodeMenuListener = nodeMenuListener;
} }
/**
* Callback listener to redefine to do an action when a node is click
*/
public interface OnNodeClickCallback { public interface OnNodeClickCallback {
void onNodeClick(PwNode node); void onNodeClick(PwNode node);
} }
public class OnNodeClickListener implements View.OnClickListener { /**
* Menu listener to redefine to do an action in menu
*/
public interface NodeMenuListener {
boolean onOpenMenuClick(PwNode node);
boolean onDeleteMenuClick(PwNode node);
}
/**
* Utility class for node listener
*/
private class OnNodeClickListener implements View.OnClickListener {
private PwNode node; private PwNode node;
OnNodeClickListener(PwNode node) { OnNodeClickListener(PwNode node) {
@@ -133,16 +208,15 @@ public class NodeAdapter extends RecyclerView.Adapter<BasicViewHolder> {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
nodePositionToUpdate = nodeSortedList.indexOf(node);
if (onNodeClickCallback != null) if (onNodeClickCallback != null)
onNodeClickCallback.onNodeClick(node); onNodeClickCallback.onNodeClick(node);
} }
} }
public interface NodeMenuListener { /**
boolean onOpenMenuClick(PwNode node); * Utility class for menu listener
boolean onDeleteMenuClick(PwNode node); */
}
private class ContextMenuBuilder implements View.OnCreateContextMenuListener { private class ContextMenuBuilder implements View.OnCreateContextMenuListener {
private PwNode node; private PwNode node;

View File

@@ -256,7 +256,7 @@ public abstract class PwDatabase {
parent = rootGroup; parent = rootGroup;
} }
parent.childGroups.add(newGroup); parent.addChildGroup(newGroup);
newGroup.setParent(parent); newGroup.setParent(parent);
groups.put(newGroup.getId(), newGroup); groups.put(newGroup.getId(), newGroup);
@@ -265,7 +265,7 @@ public abstract class PwDatabase {
public void removeGroupFrom(PwGroup remove, PwGroup parent) { public void removeGroupFrom(PwGroup remove, PwGroup parent) {
// Remove tree from parent tree // Remove tree from parent tree
parent.childGroups.remove(remove); parent.removeChildGroup(remove);
groups.remove(remove.getId()); groups.remove(remove.getId());
} }
@@ -273,7 +273,7 @@ public abstract class PwDatabase {
public void addEntryTo(PwEntry newEntry, PwGroup parent) { public void addEntryTo(PwEntry newEntry, PwGroup parent) {
// Add entry to parent // Add entry to parent
if (parent != null) { if (parent != null) {
parent.childEntries.add(newEntry); parent.addChildEntry(newEntry);
} }
newEntry.setParent(parent); newEntry.setParent(parent);
@@ -283,7 +283,7 @@ public abstract class PwDatabase {
public void removeEntryFrom(PwEntry remove, PwGroup parent) { public void removeEntryFrom(PwEntry remove, PwGroup parent) {
// Remove entry for parent // Remove entry for parent
if (parent != null) { if (parent != null) {
parent.childEntries.remove(remove); parent.removeChildEntry(remove);
} }
entries.remove(remove.getUUID()); entries.remove(remove.getUUID());
} }

View File

@@ -179,12 +179,12 @@ public class PwDatabaseV3 extends PwDatabase {
currentGroup.childEntries = getEntries(currentGroup); currentGroup.childEntries = getEntries(currentGroup);
// set parent in child entries // set parent in child entries
for (int i = 0; i < currentGroup.childEntries.size(); i++) { for (int i = 0; i < currentGroup.numbersOfChildEntries(); i++) {
PwEntryV3 entry = (PwEntryV3) currentGroup.childEntries.get(i); PwEntryV3 entry = (PwEntryV3) currentGroup.childEntries.get(i);
entry.parent = currentGroup; entry.parent = currentGroup;
} }
// recursively construct child groups // recursively construct child groups
for (int i = 0; i < currentGroup.childGroups.size(); i++) { for (int i = 0; i < currentGroup.numbersOfChildGroups(); i++) {
PwGroupV3 grp = (PwGroupV3) currentGroup.childGroups.get(i); PwGroupV3 grp = (PwGroupV3) currentGroup.childGroups.get(i);
grp.parent = currentGroup; grp.parent = currentGroup;
constructTree((PwGroupV3) currentGroup.childGroups.get(i)); constructTree((PwGroupV3) currentGroup.childGroups.get(i));

View File

@@ -28,20 +28,8 @@ import java.util.UUID;
public abstract class PwEntry extends PwNode implements Cloneable { public abstract class PwEntry extends PwNode implements Cloneable {
protected static final String PMS_TAN_ENTRY = "<TAN>"; protected static final String PMS_TAN_ENTRY = "<TAN>";
public static class EntryNameComparator implements Comparator<PwEntry> {
public int compare(PwEntry object1, PwEntry object2) {
return object1.getTitle().compareToIgnoreCase(object2.getTitle());
}
}
public PwIconStandard icon = PwIconStandard.FIRST; public PwIconStandard icon = PwIconStandard.FIRST;
public PwEntry() {
}
public static PwEntry getInstance(PwGroup parent) { public static PwEntry getInstance(PwGroup parent) {
return PwEntry.getInstance(parent, true, true); return PwEntry.getInstance(parent, true, true);
@@ -183,4 +171,53 @@ public abstract class PwEntry extends PwNode implements Cloneable {
return false; return false;
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PwEntry pwEntry = (PwEntry) o;
return isSameType(pwEntry)
&& (getUUID() != null ? getUUID().equals(pwEntry.getUUID()) : pwEntry.getUUID() == null);
}
@Override
public int hashCode() {
return getUUID() != null ? getUUID().hashCode() : 0;
}
/**
* Comparator of Entry by Name
*/
public static class EntryNameComparator implements Comparator<PwEntry> {
public int compare(PwEntry object1, PwEntry object2) {
if (object1.equals(object2))
return 0;
int entryTitleComp = object1.getTitle().compareToIgnoreCase(object2.getTitle());
// If same title, can be different
if (entryTitleComp == 0) {
return object1.hashCode() - object2.hashCode();
}
return entryTitleComp;
}
}
/**
* Comparator of Entry by Creation
*/
public static class EntryCreationComparator implements Comparator<PwEntry> {
public int compare(PwEntry object1, PwEntry object2) {
if (object1.equals(object2))
return 0;
int entryCreationComp = object1.getCreationTime().compareTo(object2.getCreationTime());
// If same creation, can be different
if (entryCreationComp == 0) {
return object1.hashCode() - object2.hashCode();
}
return entryCreationComp;
}
}
} }

View File

@@ -19,31 +19,51 @@
*/ */
package com.keepassdroid.database; package com.keepassdroid.database;
import com.keepassdroid.utils.StrUtil;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import com.keepassdroid.utils.StrUtil;
public abstract class PwGroup extends PwNode { public abstract class PwGroup extends PwNode {
// TODO Change dependency and make private
public List<PwGroup> childGroups = new ArrayList<>(); public List<PwGroup> childGroups = new ArrayList<>();
public List<PwEntry> childEntries = new ArrayList<>(); public List<PwEntry> childEntries = new ArrayList<>();
public String name = ""; public String name = "";
public PwIconStandard icon; public PwIconStandard icon;
@Override private List<PwNode> children = new ArrayList<>();
public List<PwNode> getDirectChildren() {
List<PwNode> children = new ArrayList<>(); public void initNewGroup(String nm, PwGroupId newId) {
children.addAll(childGroups); setId(newId);
children.addAll(childEntries); name = nm;
return children;
} }
@Override public void addChildGroup(PwGroup group) {
public int numberOfDirectChildren() { this.childGroups.add(group);
return childGroups.size() + childEntries.size(); }
public void addChildEntry(PwEntry entry) {
this.childEntries.add(entry);
}
public void removeChildGroup(PwGroup group) {
this.childGroups.remove(group);
}
public void removeChildEntry(PwEntry entry) {
this.childEntries.remove(entry);
}
public int numbersOfChildGroups() {
return childGroups.size();
}
public int numbersOfChildEntries() {
return childEntries.size();
} }
@Override @Override
@@ -51,8 +71,37 @@ public abstract class PwGroup extends PwNode {
return Type.GROUP; return Type.GROUP;
} }
/**
* @return List of direct children (one level below) as PwNode
*/
public List<PwNode> getDirectChildren() {
children.clear();
children.addAll(childGroups);
children.addAll(childEntries);
return children;
}
/**
* Number of direct elements in Node (one level below)
* @return Size of child elements, default is 0
*/
public int numberOfDirectChildren() {
return childGroups.size() + childEntries.size();
}
public abstract PwGroup getParent(); public abstract PwGroup getParent();
public abstract void setParent(PwGroup parent); public abstract void setParent(PwGroup parent);
public boolean isContainedIn(PwGroup container) {
PwGroup cur = this;
while (cur != null) {
if (cur == container) {
return true;
}
cur = cur.getParent();
}
return false;
}
public abstract PwGroupId getId(); public abstract PwGroupId getId();
public abstract void setId(PwGroupId id); public abstract void setId(PwGroupId id);
@@ -69,44 +118,13 @@ public abstract class PwGroup extends PwNode {
public PwIcon getIcon() { public PwIcon getIcon() {
return icon; return icon;
} }
public static class GroupNameComparator implements Comparator<PwGroup> {
public int compare(PwGroup object1, PwGroup object2) {
return object1.getName().compareToIgnoreCase(object2.getName());
}
}
public abstract void setLastAccessTime(Date date); public abstract void setLastAccessTime(Date date);
public abstract void setLastModificationTime(Date date); public abstract void setLastModificationTime(Date date);
public void sortEntriesByName() {
Collections.sort(childEntries, new PwEntry.EntryNameComparator());
}
public void initNewGroup(String nm, PwGroupId newId) {
setId(newId);
name = nm;
}
public boolean isContainedIn(PwGroup container) {
PwGroup cur = this;
while (cur != null) {
if (cur == container) {
return true;
}
cur = cur.getParent();
}
return false;
}
public void touch(boolean modified, boolean touchParents) { public void touch(boolean modified, boolean touchParents) {
Date now = new Date(); Date now = new Date();
setLastAccessTime(now); setLastAccessTime(now);
if (modified) { if (modified) {
@@ -119,7 +137,6 @@ public abstract class PwGroup extends PwNode {
} }
} }
public void searchEntries(SearchParameters sp, List<PwEntry> listStorage) { public void searchEntries(SearchParameters sp, List<PwEntry> listStorage) {
if (sp == null) { return; } if (sp == null) { return; }
if (listStorage == null) { return; } if (listStorage == null) { return; }
@@ -166,7 +183,6 @@ public abstract class PwGroup extends PwNode {
complement.add(entry); complement.add(entry);
} }
} }
pg = complement; pg = complement;
} }
else { else {
@@ -200,19 +216,64 @@ public abstract class PwGroup extends PwNode {
if (entryHandler != null) { if (entryHandler != null) {
for (PwEntry entry : childEntries) { for (PwEntry entry : childEntries) {
if (!entryHandler.operate(entry)) return false; if (!entryHandler.operate(entry)) return false;
} }
} }
for (PwGroup group : childGroups) { for (PwGroup group : childGroups) {
if ((groupHandler != null) && !groupHandler.operate(group)) return false; if ((groupHandler != null) && !groupHandler.operate(group)) return false;
group.preOrderTraverseTree(groupHandler, entryHandler); group.preOrderTraverseTree(groupHandler, entryHandler);
} }
return true; return true;
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PwGroup pwGroup = (PwGroup) o;
return isSameType(pwGroup)
&& (getId() != null ? getId().equals(pwGroup.getId()) : pwGroup.getId() == null);
}
@Override
public int hashCode() {
PwGroupId groupId = getId();
return groupId != null ? groupId.hashCode() : 0;
}
/**
* Group comparator by name
*/
public static class GroupNameComparator implements Comparator<PwGroup> {
public int compare(PwGroup object1, PwGroup object2) {
if (object1.equals(object2))
return 0;
int groupNameComp = object1.getName().compareToIgnoreCase(object2.getName());
// If same name, can be different
if (groupNameComp == 0) {
return object1.hashCode() - object2.hashCode();
}
return groupNameComp;
}
}
/**
* Group comparator by name
*/
public static class GroupCreationComparator implements Comparator<PwGroup> {
public int compare(PwGroup object1, PwGroup object2) {
if (object1.equals(object2))
return 0;
int groupCreationComp = object1.getCreationTime().compareTo(object2.getCreationTime());
// If same creation, can be different
if (groupCreationComp == 0) {
return object1.hashCode() - object2.hashCode();
}
return groupCreationComp;
}
}
} }

View File

@@ -39,7 +39,7 @@ public class PwGroupIdV3 extends PwGroupId {
@Override @Override
public int hashCode() { public int hashCode() {
Integer i = Integer.valueOf(id); Integer i = id;
return i.hashCode(); return i.hashCode();
} }

View File

@@ -34,8 +34,6 @@ import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
/** /**
* @author Brian Pellin <bpellin@gmail.com> * @author Brian Pellin <bpellin@gmail.com>
* @author Naomaru Itoi <nao@phoneid.org> * @author Naomaru Itoi <nao@phoneid.org>
@@ -43,8 +41,6 @@ import java.util.List;
* @author Dominik Reichl <dominik.reichl@t-online.de> * @author Dominik Reichl <dominik.reichl@t-online.de>
*/ */
public class PwGroupV3 extends PwGroup { public class PwGroupV3 extends PwGroup {
public PwGroupV3() {
}
public String toString() { public String toString() {
return name; return name;
@@ -155,4 +151,11 @@ public class PwGroupV3 extends PwGroup {
tLastMod = new PwDate(date); tLastMod = new PwDate(date);
} }
@Override
public Date getCreationTime() {
if(tCreation != null)
return tCreation.getJDate();
else
return new Date();
}
} }

View File

@@ -48,9 +48,7 @@ public class PwGroupV4 extends PwGroup implements ITimeLogger {
private long usageCount = 0; private long usageCount = 0;
public Map<String, String> customData = new HashMap<String, String>(); public Map<String, String> customData = new HashMap<String, String>();
public PwGroupV4() { public PwGroupV4() {}
}
public PwGroupV4(boolean createUUID, boolean setTimes, String name, PwIconStandard icon) { public PwGroupV4(boolean createUUID, boolean setTimes, String name, PwIconStandard icon) {
if (createUUID) { if (createUUID) {
@@ -86,8 +84,8 @@ public class PwGroupV4 extends PwGroup implements ITimeLogger {
public void AddEntry(PwEntryV4 pe, boolean takeOwnership, boolean updateLocationChanged) { public void AddEntry(PwEntryV4 pe, boolean takeOwnership, boolean updateLocationChanged) {
assert(pe != null); assert(pe != null);
childEntries.add(pe); addChildEntry(pe);
if ( takeOwnership ) pe.parent = this; if ( takeOwnership ) pe.parent = this;
@@ -102,7 +100,7 @@ public class PwGroupV4 extends PwGroup implements ITimeLogger {
public void buildChildGroupsRecursive(List<PwGroup> list) { public void buildChildGroupsRecursive(List<PwGroup> list) {
list.add(this); list.add(this);
for ( int i = 0; i < childGroups.size(); i++) { for ( int i = 0; i < numbersOfChildGroups(); i++) {
PwGroupV4 child = (PwGroupV4) childGroups.get(i); PwGroupV4 child = (PwGroupV4) childGroups.get(i);
child.buildChildGroupsRecursive(list); child.buildChildGroupsRecursive(list);
@@ -110,11 +108,11 @@ public class PwGroupV4 extends PwGroup implements ITimeLogger {
} }
public void buildChildEntriesRecursive(List<PwEntry> list) { public void buildChildEntriesRecursive(List<PwEntry> list) {
for ( int i = 0; i < childEntries.size(); i++ ) { for ( int i = 0; i < numbersOfChildEntries(); i++ ) {
list.add(childEntries.get(i)); list.add(childEntries.get(i));
} }
for ( int i = 0; i < childGroups.size(); i++ ) { for ( int i = 0; i < numbersOfChildGroups(); i++ ) {
PwGroupV4 child = (PwGroupV4) childGroups.get(i); PwGroupV4 child = (PwGroupV4) childGroups.get(i);
child.buildChildEntriesRecursive(list); child.buildChildEntriesRecursive(list);
} }

View File

@@ -20,71 +20,133 @@
*/ */
package com.keepassdroid.database; package com.keepassdroid.database;
import android.support.annotation.NonNull;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.Comparator;
import java.util.List; import java.util.Date;
/** /**
* Abstract class who manage Groups and Entries * Abstract class who manage Groups and Entries
*/ */
public abstract class PwNode implements Comparable<PwNode>, Serializable { public abstract class PwNode implements Serializable {
/** /**
* Get the type of Node * Type of available Nodes
*/
public enum Type {
GROUP, ENTRY
}
/**
* @return Type of Node
*/ */
public abstract Type getType(); public abstract Type getType();
/** /**
* @return title to display as view * @return Title to display as view
*/ */
public abstract String getDisplayTitle(); public abstract String getDisplayTitle();
/**
* @return Visual icon
*/
public abstract PwIcon getIcon(); public abstract PwIcon getIcon();
/** /**
* @return List of direct children (one level below) as PwNode * @return Creation date and time of the node
*/ */
public List<PwNode> getDirectChildren() { public abstract Date getCreationTime();
return new ArrayList<>();
}
public PwNode getDirectChildAt(int position) { /**
return getDirectChildren().get(position); * If the content (type, title, icon) is visually the same
* @param o Node to compare
* @return True if visually as o
*/
public boolean isContentVisuallyTheSame(PwNode o) {
return getType().equals(o.getType())
&& getDisplayTitle().equals(o.getDisplayTitle())
&& getIcon().equals(o.getIcon());
} }
/** /**
* Number of direct elements in Node (one level below) * Define if it's the same type of another node
* @return Size of child elements, default is 0 * @param otherNode The other node to test
* @return true if both have the same type
*/ */
public int numberOfDirectChildren() { boolean isSameType(PwNode otherNode) {
return getDirectChildren().size(); return getType() != null ? getType().equals(otherNode.getType()) : otherNode.getType() == null;
} }
@Override /**
public int compareTo(@NonNull PwNode o) { * Comparator of Node by Name, Groups first, Entries second
if (this instanceof PwGroup) { */
if (o instanceof PwGroup) { public static class NodeNameComparator implements Comparator<PwNode> {
return new PwGroup.GroupNameComparator().compare((PwGroup) this, (PwGroup) o); public int compare(PwNode object1, PwNode object2) {
} else if (o instanceof PwEntry) { if (object1.equals(object2))
return -1; return 0;
} else {
return -1; if (object1 instanceof PwGroup) {
} if (object2 instanceof PwGroup) {
} else if (this instanceof PwEntry) { return new PwGroup.GroupNameComparator()
if(o instanceof PwEntry) { .compare((PwGroup) object1, (PwGroup) object2);
return new PwEntry.EntryNameComparator().compare((PwEntry) this, (PwEntry) o); } else if (object2 instanceof PwEntry) {
} else if (o instanceof PwGroup) { return -1;
return 1; } else {
} else { return -1;
return 1; }
} else if (object1 instanceof PwEntry) {
if(object2 instanceof PwEntry) {
return new PwEntry.EntryNameComparator()
.compare((PwEntry) object1, (PwEntry) object2);
} else if (object2 instanceof PwGroup) {
return 1;
} else {
return -1;
}
} }
int nodeNameComp = object1.getDisplayTitle()
.compareToIgnoreCase(object2.getDisplayTitle());
// If same name, can be different
if (nodeNameComp == 0)
return object1.hashCode() - object2.hashCode();
return nodeNameComp;
} }
return this.getDisplayTitle().compareToIgnoreCase(o.getDisplayTitle());
} }
public enum Type { /**
GROUP, ENTRY * Comparator of node by creation, Groups first, Entries second
*/
public static class NodeCreationComparator implements Comparator<PwNode> {
@Override
public int compare(PwNode object1, PwNode object2) {
if (object1.equals(object2))
return 0;
if (object1 instanceof PwGroup) {
if (object2 instanceof PwGroup) {
return new PwGroup.GroupCreationComparator()
.compare((PwGroup) object1, (PwGroup) object2);
} else if (object2 instanceof PwEntry) {
return -1;
} else {
return -1;
}
} else if (object1 instanceof PwEntry) {
if(object2 instanceof PwEntry) {
return new PwEntry.EntryCreationComparator()
.compare((PwEntry) object1, (PwEntry) object2);
} else if (object2 instanceof PwGroup) {
return 1;
} else {
return -1;
}
}
int nodeCreationComp = object1.getCreationTime()
.compareTo(object2.getCreationTime());
// If same creation, can be different
if (nodeCreationComp == 0) {
return object1.hashCode() - object2.hashCode();
}
return nodeCreationComp;
}
} }
} }

View File

@@ -80,7 +80,7 @@ public class DeleteGroup extends RunnableOnFinish {
// Remove from parent // Remove from parent
PwGroup parent = mGroup.getParent(); PwGroup parent = mGroup.getParent();
if ( parent != null ) { if ( parent != null ) {
parent.childGroups.remove(mGroup); parent.removeChildGroup(mGroup);
} }
// Remove from PwDatabaseV3 // Remove from PwDatabaseV3

View File

@@ -71,16 +71,11 @@ public class UpdateEntry extends RunnableOnFinish {
public void run() { public void run() {
if ( mSuccess ) { if ( mSuccess ) {
// Mark group dirty if title or icon changes // Mark group dirty if title or icon changes
// TODO CHange if not equal... Why only title and Icon ? if ( ! mBackup.isContentVisuallyTheSame(mNewE) ) {
if ( ! mBackup.getTitle().equals(mNewE.getTitle()) || ! mBackup.getIcon().equals(mNewE.getIcon()) ) {
PwGroup parent = mBackup.getParent(); PwGroup parent = mBackup.getParent();
if ( parent != null ) { if ( parent != null ) {
// Resort entries
parent.sortEntriesByName();
// Mark parent group dirty // Mark parent group dirty
mDb.dirty.add(parent); mDb.dirty.add(parent);
} }
} }
} else { } else {

View File

@@ -260,7 +260,7 @@ public class PwDbV3Output extends PwDbOutput {
groupList.add(group); groupList.add(group);
// Recurse over children // Recurse over children
for ( int i = 0; i < group.childGroups.size(); i++ ) { for ( int i = 0; i < group.numbersOfChildGroups(); i++ ) {
sortGroup((PwGroupV3) group.childGroups.get(i), groupList); sortGroup((PwGroupV3) group.childGroups.get(i), groupList);
} }
} }

View File

@@ -99,7 +99,7 @@ public class SearchResultsActivity extends GroupBaseActivity {
private void performSearch(String query) { private void performSearch(String query) {
mGroup = mDb.Search(query.trim()); mGroup = mDb.Search(query.trim());
if ( mGroup == null || mGroup.childEntries.size() < 1 ) { if ( mGroup == null || mGroup.numbersOfChildEntries() < 1 ) {
listView.setVisibility(View.GONE); listView.setVisibility(View.GONE);
notFoundView.setVisibility(View.VISIBLE); notFoundView.setVisibility(View.VISIBLE);
} else { } else {