mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Merge branch 'feature/ListNodesFragment' into develop
This commit is contained in:
@@ -19,8 +19,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.tests.database;
|
package com.kunzisoft.keepass.tests.database;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
@@ -30,9 +28,11 @@ import com.kunzisoft.keepass.database.PwDatabaseV3;
|
|||||||
import com.kunzisoft.keepass.database.PwEntry;
|
import com.kunzisoft.keepass.database.PwEntry;
|
||||||
import com.kunzisoft.keepass.database.PwEntryV3;
|
import com.kunzisoft.keepass.database.PwEntryV3;
|
||||||
import com.kunzisoft.keepass.database.PwGroup;
|
import com.kunzisoft.keepass.database.PwGroup;
|
||||||
import com.kunzisoft.keepass.database.action.DeleteGroupRunnable;
|
import com.kunzisoft.keepass.database.action.node.DeleteGroupRunnable;
|
||||||
import com.kunzisoft.keepass.search.SearchDbHelper;
|
import com.kunzisoft.keepass.search.SearchDbHelper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class DeleteEntry extends AndroidTestCase {
|
public class DeleteEntry extends AndroidTestCase {
|
||||||
private static final String GROUP1_NAME = "Group1";
|
private static final String GROUP1_NAME = "Group1";
|
||||||
private static final String ENTRY1_NAME = "Test1";
|
private static final String ENTRY1_NAME = "Test1";
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import android.content.Intent;
|
|||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
@@ -49,10 +48,11 @@ import com.kunzisoft.keepass.database.PwEntry;
|
|||||||
import com.kunzisoft.keepass.database.PwGroup;
|
import com.kunzisoft.keepass.database.PwGroup;
|
||||||
import com.kunzisoft.keepass.database.PwGroupId;
|
import com.kunzisoft.keepass.database.PwGroupId;
|
||||||
import com.kunzisoft.keepass.database.PwIconStandard;
|
import com.kunzisoft.keepass.database.PwIconStandard;
|
||||||
import com.kunzisoft.keepass.database.action.AddEntryRunnable;
|
import com.kunzisoft.keepass.database.PwNode;
|
||||||
import com.kunzisoft.keepass.database.action.OnFinishRunnable;
|
|
||||||
import com.kunzisoft.keepass.database.action.RunnableOnFinish;
|
import com.kunzisoft.keepass.database.action.RunnableOnFinish;
|
||||||
import com.kunzisoft.keepass.database.action.UpdateEntryRunnable;
|
import com.kunzisoft.keepass.database.action.node.AddEntryRunnable;
|
||||||
|
import com.kunzisoft.keepass.database.action.node.AfterActionNodeOnFinish;
|
||||||
|
import com.kunzisoft.keepass.database.action.node.UpdateEntryRunnable;
|
||||||
import com.kunzisoft.keepass.database.security.ProtectedString;
|
import com.kunzisoft.keepass.database.security.ProtectedString;
|
||||||
import com.kunzisoft.keepass.dialogs.GeneratePasswordDialogFragment;
|
import com.kunzisoft.keepass.dialogs.GeneratePasswordDialogFragment;
|
||||||
import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment;
|
import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment;
|
||||||
@@ -67,6 +67,8 @@ import com.kunzisoft.keepass.view.EntryEditCustomField;
|
|||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static com.kunzisoft.keepass.dialogs.IconPickerDialogFragment.UNDEFINED_ICON_ID;
|
import static com.kunzisoft.keepass.dialogs.IconPickerDialogFragment.UNDEFINED_ICON_ID;
|
||||||
|
|
||||||
public class EntryEditActivity extends LockingHideActivity
|
public class EntryEditActivity extends LockingHideActivity
|
||||||
@@ -251,7 +253,7 @@ public class EntryEditActivity extends LockingHideActivity
|
|||||||
mCallbackNewEntry = populateNewEntry();
|
mCallbackNewEntry = populateNewEntry();
|
||||||
|
|
||||||
// Open a progress dialog and save entry
|
// Open a progress dialog and save entry
|
||||||
OnFinishRunnable onFinish = new AfterSave();
|
AfterActionNodeOnFinish onFinish = new AfterSave();
|
||||||
EntryEditActivity act = EntryEditActivity.this;
|
EntryEditActivity act = EntryEditActivity.this;
|
||||||
RunnableOnFinish task;
|
RunnableOnFinish task;
|
||||||
if ( mIsNew ) {
|
if ( mIsNew ) {
|
||||||
@@ -560,14 +562,10 @@ public class EntryEditActivity extends LockingHideActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class AfterSave extends OnFinishRunnable {
|
private final class AfterSave extends AfterActionNodeOnFinish {
|
||||||
|
|
||||||
AfterSave() {
|
|
||||||
super(new Handler());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run(@Nullable PwNode oldNode, @Nullable PwNode newNode) {
|
||||||
runOnUiThread(() -> {
|
runOnUiThread(() -> {
|
||||||
if ( mSuccess ) {
|
if ( mSuccess ) {
|
||||||
finish();
|
finish();
|
||||||
@@ -578,6 +576,6 @@ public class EntryEditActivity extends LockingHideActivity
|
|||||||
SaveDatabaseProgressTaskDialogFragment.stop(EntryEditActivity.this);
|
SaveDatabaseProgressTaskDialogFragment.stop(EntryEditActivity.this);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ import android.os.Build;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.RequiresApi;
|
import android.support.annotation.RequiresApi;
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.support.v7.widget.SearchView;
|
import android.support.v7.widget.SearchView;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -50,17 +50,21 @@ import com.kunzisoft.keepass.adapters.NodeAdapter;
|
|||||||
import com.kunzisoft.keepass.app.App;
|
import com.kunzisoft.keepass.app.App;
|
||||||
import com.kunzisoft.keepass.autofill.AutofillHelper;
|
import com.kunzisoft.keepass.autofill.AutofillHelper;
|
||||||
import com.kunzisoft.keepass.database.Database;
|
import com.kunzisoft.keepass.database.Database;
|
||||||
|
import com.kunzisoft.keepass.database.PwDatabase;
|
||||||
import com.kunzisoft.keepass.database.PwEntry;
|
import com.kunzisoft.keepass.database.PwEntry;
|
||||||
import com.kunzisoft.keepass.database.PwGroup;
|
import com.kunzisoft.keepass.database.PwGroup;
|
||||||
import com.kunzisoft.keepass.database.PwGroupId;
|
import com.kunzisoft.keepass.database.PwGroupId;
|
||||||
import com.kunzisoft.keepass.database.PwIcon;
|
import com.kunzisoft.keepass.database.PwIcon;
|
||||||
import com.kunzisoft.keepass.database.PwIconStandard;
|
import com.kunzisoft.keepass.database.PwIconStandard;
|
||||||
import com.kunzisoft.keepass.database.PwNode;
|
import com.kunzisoft.keepass.database.PwNode;
|
||||||
import com.kunzisoft.keepass.database.SortNodeEnum;
|
import com.kunzisoft.keepass.database.action.node.AddGroupRunnable;
|
||||||
import com.kunzisoft.keepass.database.action.AddGroupRunnable;
|
import com.kunzisoft.keepass.database.action.node.AfterActionNodeOnFinish;
|
||||||
import com.kunzisoft.keepass.database.action.DeleteEntryRunnable;
|
import com.kunzisoft.keepass.database.action.node.CopyEntryRunnable;
|
||||||
import com.kunzisoft.keepass.database.action.DeleteGroupRunnable;
|
import com.kunzisoft.keepass.database.action.node.DeleteEntryRunnable;
|
||||||
import com.kunzisoft.keepass.database.action.UpdateGroupRunnable;
|
import com.kunzisoft.keepass.database.action.node.DeleteGroupRunnable;
|
||||||
|
import com.kunzisoft.keepass.database.action.node.MoveEntryRunnable;
|
||||||
|
import com.kunzisoft.keepass.database.action.node.MoveGroupRunnable;
|
||||||
|
import com.kunzisoft.keepass.database.action.node.UpdateGroupRunnable;
|
||||||
import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment;
|
import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment;
|
||||||
import com.kunzisoft.keepass.dialogs.GroupEditDialogFragment;
|
import com.kunzisoft.keepass.dialogs.GroupEditDialogFragment;
|
||||||
import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment;
|
import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment;
|
||||||
@@ -69,16 +73,25 @@ import com.kunzisoft.keepass.icons.IconPackChooser;
|
|||||||
import com.kunzisoft.keepass.search.SearchResultsActivity;
|
import com.kunzisoft.keepass.search.SearchResultsActivity;
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil;
|
import com.kunzisoft.keepass.settings.PreferencesUtil;
|
||||||
import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment;
|
import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment;
|
||||||
|
import com.kunzisoft.keepass.tasks.UIToastTask;
|
||||||
import com.kunzisoft.keepass.tasks.UpdateProgressTaskStatus;
|
import com.kunzisoft.keepass.tasks.UpdateProgressTaskStatus;
|
||||||
import com.kunzisoft.keepass.view.AddNodeButtonView;
|
import com.kunzisoft.keepass.view.AddNodeButtonView;
|
||||||
|
|
||||||
public class GroupActivity extends ListNodesActivity
|
import net.cachapa.expandablelayout.ExpandableLayout;
|
||||||
implements GroupEditDialogFragment.EditGroupListener, IconPickerDialogFragment.IconPickerListener {
|
|
||||||
|
|
||||||
private static final String GROUP_ID_KEY = "GROUP_ID_KEY";
|
public class GroupActivity extends ListNodesActivity
|
||||||
|
implements GroupEditDialogFragment.EditGroupListener,
|
||||||
|
IconPickerDialogFragment.IconPickerListener,
|
||||||
|
NodeAdapter.NodeMenuListener,
|
||||||
|
ListNodesFragment.OnScrollListener {
|
||||||
|
|
||||||
|
private static final String TAG = GroupActivity.class.getName();
|
||||||
|
|
||||||
private Toolbar toolbar;
|
private Toolbar toolbar;
|
||||||
|
|
||||||
|
private ExpandableLayout toolbarPasteExpandableLayout;
|
||||||
|
private Toolbar toolbarPaste;
|
||||||
|
|
||||||
private ImageView iconView;
|
private ImageView iconView;
|
||||||
private AddNodeButtonView addNodeButtonView;
|
private AddNodeButtonView addNodeButtonView;
|
||||||
|
|
||||||
@@ -87,13 +100,15 @@ public class GroupActivity extends ListNodesActivity
|
|||||||
protected boolean isRoot = false;
|
protected boolean isRoot = false;
|
||||||
protected boolean readOnly = false;
|
protected boolean readOnly = false;
|
||||||
|
|
||||||
private static final String TAG = "Group Activity:";
|
|
||||||
|
|
||||||
private static final String OLD_GROUP_TO_UPDATE_KEY = "OLD_GROUP_TO_UPDATE_KEY";
|
private static final String OLD_GROUP_TO_UPDATE_KEY = "OLD_GROUP_TO_UPDATE_KEY";
|
||||||
|
private static final String NODE_TO_COPY_KEY = "NODE_TO_COPY_KEY";
|
||||||
|
private static final String NODE_TO_MOVE_KEY = "NODE_TO_MOVE_KEY";
|
||||||
private PwGroup oldGroupToUpdate;
|
private PwGroup oldGroupToUpdate;
|
||||||
|
private PwNode nodeToCopy;
|
||||||
|
private PwNode nodeToMove;
|
||||||
|
|
||||||
public static void launch(Activity act) {
|
public static void launch(Activity act) {
|
||||||
recordFirstTimeBeforeLaunch(act);
|
startRecordTime(act);
|
||||||
launch(act, (PwGroup) null);
|
launch(act, (PwGroup) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +125,7 @@ public class GroupActivity extends ListNodesActivity
|
|||||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
public static void launch(Activity act, AssistStructure assistStructure) {
|
public static void launch(Activity act, AssistStructure assistStructure) {
|
||||||
if ( assistStructure != null ) {
|
if ( assistStructure != null ) {
|
||||||
recordFirstTimeBeforeLaunch(act);
|
startRecordTime(act);
|
||||||
launch(act, null, assistStructure);
|
launch(act, null, assistStructure);
|
||||||
} else {
|
} else {
|
||||||
launch(act);
|
launch(act);
|
||||||
@@ -136,35 +151,51 @@ public class GroupActivity extends ListNodesActivity
|
|||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
Log.w(TAG, "Retrieved tree");
|
Log.i(TAG, "Started creating tree");
|
||||||
if ( mCurrentGroup == null ) {
|
if ( mCurrentGroup == null ) {
|
||||||
Log.w(TAG, "Group was null");
|
Log.w(TAG, "Group was null");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (savedInstanceState != null
|
// Construct main view
|
||||||
&& savedInstanceState.containsKey(OLD_GROUP_TO_UPDATE_KEY)) {
|
|
||||||
oldGroupToUpdate = (PwGroup) savedInstanceState.getSerializable(OLD_GROUP_TO_UPDATE_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct main view
|
|
||||||
setContentView(getLayoutInflater().inflate(R.layout.list_nodes_with_add_button, null));
|
setContentView(getLayoutInflater().inflate(R.layout.list_nodes_with_add_button, null));
|
||||||
|
|
||||||
|
attachFragmentToContentView();
|
||||||
|
|
||||||
iconView = findViewById(R.id.icon);
|
iconView = findViewById(R.id.icon);
|
||||||
addNodeButtonView = findViewById(R.id.add_node_button);
|
addNodeButtonView = findViewById(R.id.add_node_button);
|
||||||
addNodeButtonView.enableAddGroup(addGroupEnabled);
|
addNodeButtonView.enableAddGroup(addGroupEnabled);
|
||||||
addNodeButtonView.enableAddEntry(addEntryEnabled);
|
addNodeButtonView.enableAddEntry(addEntryEnabled);
|
||||||
// Hide when scroll
|
|
||||||
RecyclerView recyclerView = findViewById(R.id.nodes_list);
|
|
||||||
recyclerView.addOnScrollListener(addNodeButtonView.hideButtonOnScrollListener());
|
|
||||||
|
|
||||||
toolbar = findViewById(R.id.toolbar);
|
toolbar = findViewById(R.id.toolbar);
|
||||||
toolbar.setTitle("");
|
toolbar.setTitle("");
|
||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(toolbar);
|
||||||
|
groupNameView = findViewById(R.id.group_name);
|
||||||
|
|
||||||
if ( mCurrentGroup.getParent() != null )
|
toolbarPasteExpandableLayout = findViewById(R.id.expandable_toolbar_paste_layout);
|
||||||
toolbar.setNavigationIcon(R.drawable.ic_arrow_up_white_24dp);
|
toolbarPaste = findViewById(R.id.toolbar_paste);
|
||||||
|
toolbarPaste.inflateMenu(R.menu.node_paste_menu);
|
||||||
|
toolbarPaste.setNavigationIcon(R.drawable.ic_arrow_left_white_24dp);
|
||||||
|
toolbarPaste.setNavigationOnClickListener(view -> {
|
||||||
|
toolbarPasteExpandableLayout.collapse();
|
||||||
|
nodeToCopy = null;
|
||||||
|
nodeToMove = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
if (savedInstanceState.containsKey(OLD_GROUP_TO_UPDATE_KEY))
|
||||||
|
oldGroupToUpdate = (PwGroup) savedInstanceState.getSerializable(OLD_GROUP_TO_UPDATE_KEY);
|
||||||
|
|
||||||
|
if (savedInstanceState.containsKey(NODE_TO_COPY_KEY)) {
|
||||||
|
nodeToCopy = (PwNode) savedInstanceState.getSerializable(NODE_TO_COPY_KEY);
|
||||||
|
toolbarPaste.setOnMenuItemClickListener(new OnCopyMenuItemClickListener());
|
||||||
|
}
|
||||||
|
else if (savedInstanceState.containsKey(NODE_TO_MOVE_KEY)) {
|
||||||
|
nodeToMove = (PwNode) savedInstanceState.getSerializable(NODE_TO_MOVE_KEY);
|
||||||
|
toolbarPaste.setOnMenuItemClickListener(new OnMoveMenuItemClickListener());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
addNodeButtonView.setAddGroupClickListener(v -> {
|
addNodeButtonView.setAddGroupClickListener(v -> {
|
||||||
GroupEditDialogFragment.build()
|
GroupEditDialogFragment.build()
|
||||||
@@ -173,24 +204,42 @@ public class GroupActivity extends ListNodesActivity
|
|||||||
});
|
});
|
||||||
addNodeButtonView.setAddEntryClickListener(v ->
|
addNodeButtonView.setAddEntryClickListener(v ->
|
||||||
EntryEditActivity.launch(GroupActivity.this, mCurrentGroup));
|
EntryEditActivity.launch(GroupActivity.this, mCurrentGroup));
|
||||||
|
|
||||||
setGroupTitle();
|
|
||||||
|
|
||||||
Log.w(TAG, "Finished creating tree");
|
Log.i(TAG, "Finished creating tree");
|
||||||
|
|
||||||
if (isRoot) {
|
if (isRoot) {
|
||||||
showWarnings();
|
showWarnings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected PwGroup initCurrentGroup() {
|
@Override
|
||||||
PwGroup currentGroup;
|
protected void onSaveInstanceState(Bundle outState) {
|
||||||
|
outState.putSerializable(GROUP_ID_KEY, mCurrentGroup.getId());
|
||||||
|
outState.putSerializable(OLD_GROUP_TO_UPDATE_KEY, oldGroupToUpdate);
|
||||||
|
if (nodeToCopy != null)
|
||||||
|
outState.putSerializable(NODE_TO_COPY_KEY, nodeToCopy);
|
||||||
|
if (nodeToMove != null)
|
||||||
|
outState.putSerializable(NODE_TO_MOVE_KEY, nodeToMove);
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PwGroup retrieveCurrentGroup(@Nullable Bundle savedInstanceState) {
|
||||||
|
|
||||||
|
PwGroupId pwGroupId = null; // TODO Parcelable
|
||||||
|
if (savedInstanceState != null
|
||||||
|
&& savedInstanceState.containsKey(GROUP_ID_KEY)) {
|
||||||
|
pwGroupId = (PwGroupId) savedInstanceState.getSerializable(GROUP_ID_KEY);
|
||||||
|
} else {
|
||||||
|
if (getIntent() != null)
|
||||||
|
pwGroupId = (PwGroupId) getIntent().getSerializableExtra(GROUP_ID_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
Database db = App.getDB();
|
Database db = App.getDB();
|
||||||
readOnly = db.isReadOnly();
|
readOnly = db.isReadOnly();
|
||||||
PwGroup root = db.getPwDatabase().getRootGroup();
|
PwGroup root = db.getPwDatabase().getRootGroup();
|
||||||
|
|
||||||
Log.w(TAG, "Creating tree view");
|
Log.w(TAG, "Creating tree view");
|
||||||
PwGroupId pwGroupId = (PwGroupId) getIntent().getSerializableExtra(GROUP_ID_KEY);
|
PwGroup currentGroup;
|
||||||
if ( pwGroupId == null ) {
|
if ( pwGroupId == null ) {
|
||||||
currentGroup = root;
|
currentGroup = root;
|
||||||
} else {
|
} else {
|
||||||
@@ -209,67 +258,212 @@ public class GroupActivity extends ListNodesActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected RecyclerView defineNodeList() {
|
public void assignToolbarElements() {
|
||||||
return (RecyclerView) findViewById(R.id.nodes_list);
|
super.assignToolbarElements();
|
||||||
|
|
||||||
|
// Assign the group icon depending of IconPack or custom icon
|
||||||
|
if ( mCurrentGroup != null ) {
|
||||||
|
if (IconPackChooser.getSelectedIconPack(this).tintable()) {
|
||||||
|
// Retrieve the textColor to tint the icon
|
||||||
|
int[] attrs = {R.attr.textColorInverse};
|
||||||
|
TypedArray ta = getTheme().obtainStyledAttributes(attrs);
|
||||||
|
int iconColor = ta.getColor(0, Color.WHITE);
|
||||||
|
App.getDB().getDrawFactory().assignDatabaseIconTo(this, iconView, mCurrentGroup.getIcon(), true, iconColor);
|
||||||
|
} else {
|
||||||
|
App.getDB().getDrawFactory().assignDatabaseIconTo(this, iconView, mCurrentGroup.getIcon());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toolbar != null) {
|
||||||
|
if ( mCurrentGroup.containsParent() )
|
||||||
|
toolbar.setNavigationIcon(R.drawable.ic_arrow_up_white_24dp);
|
||||||
|
else {
|
||||||
|
toolbar.setNavigationIcon(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addOptionsToAdapter(NodeAdapter nodeAdapter) {
|
public void onScrolled(int dy) {
|
||||||
super.addOptionsToAdapter(nodeAdapter);
|
if (addNodeButtonView != null)
|
||||||
|
addNodeButtonView.hideButtonOnScrollListener(dy);
|
||||||
|
}
|
||||||
|
|
||||||
nodeAdapter.setActivateContextMenu(true);
|
@Override
|
||||||
nodeAdapter.setNodeMenuListener(new NodeAdapter.NodeMenuListener() {
|
public boolean onOpenMenuClick(PwNode node) {
|
||||||
@Override
|
onNodeClick(node);
|
||||||
public boolean onOpenMenuClick(PwNode node) {
|
return true;
|
||||||
switch (node.getType()) {
|
}
|
||||||
case GROUP:
|
|
||||||
GroupActivity.launch(GroupActivity.this, (PwGroup) node);
|
|
||||||
break;
|
|
||||||
case ENTRY:
|
|
||||||
EntryActivity.launch(GroupActivity.this, (PwEntry) node);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onEditMenuClick(PwNode node) {
|
public boolean onEditMenuClick(PwNode node) {
|
||||||
switch (node.getType()) {
|
switch (node.getType()) {
|
||||||
case GROUP:
|
case GROUP:
|
||||||
oldGroupToUpdate = (PwGroup) node;
|
oldGroupToUpdate = (PwGroup) node;
|
||||||
GroupEditDialogFragment.build(node)
|
GroupEditDialogFragment.build(node)
|
||||||
.show(getSupportFragmentManager(),
|
.show(getSupportFragmentManager(),
|
||||||
GroupEditDialogFragment.TAG_CREATE_GROUP);
|
GroupEditDialogFragment.TAG_CREATE_GROUP);
|
||||||
break;
|
break;
|
||||||
case ENTRY:
|
case ENTRY:
|
||||||
EntryEditActivity.launch(GroupActivity.this, (PwEntry) node);
|
EntryEditActivity.launch(GroupActivity.this, (PwEntry) node);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onDeleteMenuClick(PwNode node) {
|
public boolean onCopyMenuClick(PwNode node) {
|
||||||
switch (node.getType()) {
|
|
||||||
case GROUP:
|
toolbarPasteExpandableLayout.expand();
|
||||||
deleteGroup((PwGroup) node);
|
nodeToCopy = node;
|
||||||
break;
|
toolbarPaste.setOnMenuItemClickListener(new OnCopyMenuItemClickListener());
|
||||||
case ENTRY:
|
return false;
|
||||||
deleteEntry((PwEntry) node);
|
}
|
||||||
break;
|
|
||||||
}
|
private class OnCopyMenuItemClickListener implements Toolbar.OnMenuItemClickListener{
|
||||||
return true;
|
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemClick(MenuItem item) {
|
||||||
|
toolbarPasteExpandableLayout.collapse();
|
||||||
|
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.menu_paste:
|
||||||
|
switch (nodeToCopy.getType()) {
|
||||||
|
case GROUP:
|
||||||
|
Log.e(TAG, "Copy not allowed for group");
|
||||||
|
break;
|
||||||
|
case ENTRY:
|
||||||
|
copyNode((PwEntry) nodeToCopy, mCurrentGroup);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nodeToCopy = null;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
});
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyNode(PwEntry entryToCopy, PwGroup newParent) {
|
||||||
|
CopyEntryRunnable task = new CopyEntryRunnable(this, App.getDB(), entryToCopy, newParent,
|
||||||
|
new AfterAddNode());
|
||||||
|
task.setUpdateProgressTaskStatus(
|
||||||
|
new UpdateProgressTaskStatus(this,
|
||||||
|
SaveDatabaseProgressTaskDialogFragment.start(
|
||||||
|
getSupportFragmentManager())
|
||||||
|
));
|
||||||
|
new Thread(task).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMoveMenuClick(PwNode node) {
|
||||||
|
|
||||||
|
toolbarPasteExpandableLayout.expand();
|
||||||
|
nodeToMove = node;
|
||||||
|
toolbarPaste.setOnMenuItemClickListener(new OnMoveMenuItemClickListener());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OnMoveMenuItemClickListener implements Toolbar.OnMenuItemClickListener{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemClick(MenuItem item) {
|
||||||
|
toolbarPasteExpandableLayout.collapse();
|
||||||
|
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.menu_paste:
|
||||||
|
switch (nodeToMove.getType()) {
|
||||||
|
case GROUP:
|
||||||
|
moveGroup((PwGroup) nodeToMove, mCurrentGroup);
|
||||||
|
break;
|
||||||
|
case ENTRY:
|
||||||
|
moveEntry((PwEntry) nodeToMove, mCurrentGroup);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nodeToMove = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void moveGroup(PwGroup groupToMove, PwGroup newParent) {
|
||||||
|
MoveGroupRunnable task = new MoveGroupRunnable(
|
||||||
|
this,
|
||||||
|
App.getDB(),
|
||||||
|
groupToMove,
|
||||||
|
newParent,
|
||||||
|
new AfterAddNode());
|
||||||
|
task.setUpdateProgressTaskStatus(
|
||||||
|
new UpdateProgressTaskStatus(this,
|
||||||
|
SaveDatabaseProgressTaskDialogFragment.start(
|
||||||
|
getSupportFragmentManager())
|
||||||
|
));
|
||||||
|
new Thread(task).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void moveEntry(PwEntry entryToMove, PwGroup newParent) {
|
||||||
|
MoveEntryRunnable task = new MoveEntryRunnable(
|
||||||
|
this,
|
||||||
|
App.getDB(),
|
||||||
|
entryToMove,
|
||||||
|
newParent,
|
||||||
|
new AfterAddNode());
|
||||||
|
task.setUpdateProgressTaskStatus(
|
||||||
|
new UpdateProgressTaskStatus(this,
|
||||||
|
SaveDatabaseProgressTaskDialogFragment.start(
|
||||||
|
getSupportFragmentManager())
|
||||||
|
));
|
||||||
|
new Thread(task).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onDeleteMenuClick(PwNode node) {
|
||||||
|
switch (node.getType()) {
|
||||||
|
case GROUP:
|
||||||
|
deleteGroup((PwGroup) node);
|
||||||
|
break;
|
||||||
|
case ENTRY:
|
||||||
|
deleteEntry((PwEntry) node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteGroup(PwGroup group) {
|
||||||
|
//TODO Verify trash recycle bin
|
||||||
|
DeleteGroupRunnable task = new DeleteGroupRunnable(
|
||||||
|
this,
|
||||||
|
App.getDB(),
|
||||||
|
group,
|
||||||
|
new AfterDeleteNode());
|
||||||
|
task.setUpdateProgressTaskStatus(
|
||||||
|
new UpdateProgressTaskStatus(this,
|
||||||
|
SaveDatabaseProgressTaskDialogFragment.start(
|
||||||
|
getSupportFragmentManager())
|
||||||
|
));
|
||||||
|
new Thread(task).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteEntry(PwEntry entry) {
|
||||||
|
DeleteEntryRunnable task = new DeleteEntryRunnable(
|
||||||
|
this,
|
||||||
|
App.getDB(),
|
||||||
|
entry,
|
||||||
|
new AfterDeleteNode());
|
||||||
|
task.setUpdateProgressTaskStatus(
|
||||||
|
new UpdateProgressTaskStatus(this,
|
||||||
|
SaveDatabaseProgressTaskDialogFragment.start(
|
||||||
|
getSupportFragmentManager())
|
||||||
|
));
|
||||||
|
new Thread(task).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
// Refresh the group icon
|
|
||||||
assignGroupIcon();
|
|
||||||
// Show button on resume
|
// Show button on resume
|
||||||
addNodeButtonView.showButton();
|
if (addNodeButtonView != null)
|
||||||
|
addNodeButtonView.showButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -279,7 +473,8 @@ public class GroupActivity extends ListNodesActivity
|
|||||||
private void checkAndPerformedEducation(Menu menu) {
|
private void checkAndPerformedEducation(Menu menu) {
|
||||||
|
|
||||||
// If no node, show education to add new one
|
// If no node, show education to add new one
|
||||||
if (mAdapter.getItemCount() <= 0) {
|
if (listNodesFragment != null
|
||||||
|
&& listNodesFragment.isEmpty()) {
|
||||||
if (!PreferencesUtil.isEducationNewNodePerformed(this)) {
|
if (!PreferencesUtil.isEducationNewNodePerformed(this)) {
|
||||||
|
|
||||||
TapTargetView.showFor(this,
|
TapTargetView.showFor(this,
|
||||||
@@ -407,56 +602,8 @@ public class GroupActivity extends ListNodesActivity
|
|||||||
protected void onStop() {
|
protected void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
// Hide button
|
// Hide button
|
||||||
addNodeButtonView.hideButton();
|
if (addNodeButtonView != null)
|
||||||
}
|
addNodeButtonView.hideButton();
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSortSelected(SortNodeEnum sortNodeEnum, boolean ascending, boolean groupsBefore, boolean recycleBinBottom) {
|
|
||||||
super.onSortSelected(sortNodeEnum, ascending, groupsBefore, recycleBinBottom);
|
|
||||||
// Show button if hide after sort
|
|
||||||
addNodeButtonView.showButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assign the group icon depending of IconPack or custom icon
|
|
||||||
*/
|
|
||||||
protected void assignGroupIcon() {
|
|
||||||
if (mCurrentGroup != null) {
|
|
||||||
if (IconPackChooser.getSelectedIconPack(this).tintable()) {
|
|
||||||
// Retrieve the textColor to tint the icon
|
|
||||||
int[] attrs = {R.attr.textColorInverse};
|
|
||||||
TypedArray ta = getTheme().obtainStyledAttributes(attrs);
|
|
||||||
int iconColor = ta.getColor(0, Color.WHITE);
|
|
||||||
App.getDB().getDrawFactory().assignDatabaseIconTo(this, iconView, mCurrentGroup.getIcon(), true, iconColor);
|
|
||||||
} else {
|
|
||||||
App.getDB().getDrawFactory().assignDatabaseIconTo(this, iconView, mCurrentGroup.getIcon());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteEntry(PwEntry entry) {
|
|
||||||
Handler handler = new Handler();
|
|
||||||
DeleteEntryRunnable task = new DeleteEntryRunnable(this, App.getDB(), entry,
|
|
||||||
new AfterDeleteNode(handler, entry));
|
|
||||||
task.setUpdateProgressTaskStatus(
|
|
||||||
new UpdateProgressTaskStatus(this,
|
|
||||||
SaveDatabaseProgressTaskDialogFragment.start(
|
|
||||||
getSupportFragmentManager())
|
|
||||||
));
|
|
||||||
new Thread(task).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteGroup(PwGroup group) {
|
|
||||||
//TODO Verify trash recycle bin
|
|
||||||
Handler handler = new Handler();
|
|
||||||
DeleteGroupRunnable task = new DeleteGroupRunnable(this, App.getDB(), group,
|
|
||||||
new AfterDeleteNode(handler, group));
|
|
||||||
task.setUpdateProgressTaskStatus(
|
|
||||||
new UpdateProgressTaskStatus(this,
|
|
||||||
SaveDatabaseProgressTaskDialogFragment.start(
|
|
||||||
getSupportFragmentManager())
|
|
||||||
));
|
|
||||||
new Thread(task).start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -564,7 +711,7 @@ public class GroupActivity extends ListNodesActivity
|
|||||||
AddGroupRunnable addGroupRunnable = new AddGroupRunnable(this,
|
AddGroupRunnable addGroupRunnable = new AddGroupRunnable(this,
|
||||||
App.getDB(),
|
App.getDB(),
|
||||||
newGroup,
|
newGroup,
|
||||||
new AfterAddNode(new Handler()));
|
new AfterAddNode());
|
||||||
addGroupRunnable.setUpdateProgressTaskStatus(
|
addGroupRunnable.setUpdateProgressTaskStatus(
|
||||||
new UpdateProgressTaskStatus(this,
|
new UpdateProgressTaskStatus(this,
|
||||||
SaveDatabaseProgressTaskDialogFragment.start(
|
SaveDatabaseProgressTaskDialogFragment.start(
|
||||||
@@ -583,14 +730,15 @@ public class GroupActivity extends ListNodesActivity
|
|||||||
} catch (Exception ignored) {} // TODO custom icon
|
} catch (Exception ignored) {} // TODO custom icon
|
||||||
updateGroup.setIcon(iconStandard);
|
updateGroup.setIcon(iconStandard);
|
||||||
|
|
||||||
mAdapter.removeNode(oldGroupToUpdate);
|
if (listNodesFragment != null)
|
||||||
|
listNodesFragment.removeNode(oldGroupToUpdate);
|
||||||
|
|
||||||
// If group updated save it in the database
|
// If group updated save it in the database
|
||||||
UpdateGroupRunnable updateGroupRunnable = new UpdateGroupRunnable(this,
|
UpdateGroupRunnable updateGroupRunnable = new UpdateGroupRunnable(this,
|
||||||
App.getDB(),
|
App.getDB(),
|
||||||
oldGroupToUpdate,
|
oldGroupToUpdate,
|
||||||
updateGroup,
|
updateGroup,
|
||||||
new AfterUpdateNode(new Handler()));
|
new AfterUpdateNode());
|
||||||
updateGroupRunnable.setUpdateProgressTaskStatus(
|
updateGroupRunnable.setUpdateProgressTaskStatus(
|
||||||
new UpdateProgressTaskStatus(this,
|
new UpdateProgressTaskStatus(this,
|
||||||
SaveDatabaseProgressTaskDialogFragment.start(
|
SaveDatabaseProgressTaskDialogFragment.start(
|
||||||
@@ -603,10 +751,79 @@ public class GroupActivity extends ListNodesActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
class AfterAddNode extends AfterActionNodeOnFinish {
|
||||||
protected void onSaveInstanceState(Bundle outState) {
|
|
||||||
outState.putSerializable(OLD_GROUP_TO_UPDATE_KEY, oldGroupToUpdate);
|
public void run(PwNode oldNode, PwNode newNode) {
|
||||||
super.onSaveInstanceState(outState);
|
super.run();
|
||||||
|
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
if (mSuccess) {
|
||||||
|
if (listNodesFragment != null)
|
||||||
|
listNodesFragment.addNode(newNode);
|
||||||
|
} else {
|
||||||
|
displayMessage(GroupActivity.this);
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveDatabaseProgressTaskDialogFragment.stop(GroupActivity.this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AfterUpdateNode extends AfterActionNodeOnFinish {
|
||||||
|
|
||||||
|
public void run(PwNode oldNode, PwNode newNode) {
|
||||||
|
super.run();
|
||||||
|
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
if (mSuccess) {
|
||||||
|
if (listNodesFragment != null)
|
||||||
|
listNodesFragment.updateNode(oldNode, newNode);
|
||||||
|
} else {
|
||||||
|
displayMessage(GroupActivity.this);
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveDatabaseProgressTaskDialogFragment.stop(GroupActivity.this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AfterDeleteNode extends AfterActionNodeOnFinish {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(PwNode oldNode, PwNode newNode) {
|
||||||
|
super.run();
|
||||||
|
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
if ( mSuccess) {
|
||||||
|
|
||||||
|
if (listNodesFragment != null)
|
||||||
|
listNodesFragment.removeNode(oldNode);
|
||||||
|
|
||||||
|
PwGroup parent = oldNode.getParent();
|
||||||
|
Database db = App.getDB();
|
||||||
|
PwDatabase database = db.getPwDatabase();
|
||||||
|
if (db.isRecycleBinAvailable() &&
|
||||||
|
db.isRecycleBinEnabled()) {
|
||||||
|
PwGroup recycleBin = database.getRecycleBin();
|
||||||
|
// Add trash if it doesn't exists
|
||||||
|
if (parent.equals(recycleBin)
|
||||||
|
&& mCurrentGroup != null
|
||||||
|
&& mCurrentGroup.getParent() == null
|
||||||
|
&& !mCurrentGroup.equals(recycleBin)) {
|
||||||
|
|
||||||
|
if (listNodesFragment != null)
|
||||||
|
listNodesFragment.addNode(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mHandler.post(new UIToastTask(GroupActivity.this, "Unrecoverable error: " + mMessage));
|
||||||
|
App.setShutdown();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveDatabaseProgressTaskDialogFragment.stop(GroupActivity.this);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -637,4 +854,16 @@ public class GroupActivity extends ListNodesActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void openGroup(PwGroup group) {
|
||||||
|
super.openGroup(group);
|
||||||
|
addNodeButtonView.showButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
super.onBackPressed();
|
||||||
|
addNodeButtonView.showButton();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,51 +22,40 @@ package com.kunzisoft.keepass.activities;
|
|||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.assist.AssistStructure;
|
import android.app.assist.AssistStructure;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.content.SharedPreferences.Editor;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.support.annotation.Nullable;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.kunzisoft.keepass.R;
|
import com.kunzisoft.keepass.R;
|
||||||
import com.kunzisoft.keepass.adapters.NodeAdapter;
|
import com.kunzisoft.keepass.adapters.NodeAdapter;
|
||||||
import com.kunzisoft.keepass.app.App;
|
import com.kunzisoft.keepass.app.App;
|
||||||
import com.kunzisoft.keepass.autofill.AutofillHelper;
|
import com.kunzisoft.keepass.autofill.AutofillHelper;
|
||||||
import com.kunzisoft.keepass.database.Database;
|
|
||||||
import com.kunzisoft.keepass.database.PwDatabase;
|
|
||||||
import com.kunzisoft.keepass.database.PwEntry;
|
import com.kunzisoft.keepass.database.PwEntry;
|
||||||
import com.kunzisoft.keepass.database.PwGroup;
|
import com.kunzisoft.keepass.database.PwGroup;
|
||||||
import com.kunzisoft.keepass.database.PwNode;
|
import com.kunzisoft.keepass.database.PwNode;
|
||||||
import com.kunzisoft.keepass.database.SortNodeEnum;
|
import com.kunzisoft.keepass.database.SortNodeEnum;
|
||||||
import com.kunzisoft.keepass.database.action.AfterActionNodeOnFinish;
|
|
||||||
import com.kunzisoft.keepass.database.action.OnFinishRunnable;
|
|
||||||
import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment;
|
import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment;
|
||||||
import com.kunzisoft.keepass.dialogs.SortDialogFragment;
|
import com.kunzisoft.keepass.dialogs.SortDialogFragment;
|
||||||
import com.kunzisoft.keepass.password.AssignPasswordHelper;
|
import com.kunzisoft.keepass.password.AssignPasswordHelper;
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil;
|
|
||||||
import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment;
|
|
||||||
import com.kunzisoft.keepass.tasks.UIToastTask;
|
|
||||||
import com.kunzisoft.keepass.utils.MenuUtil;
|
import com.kunzisoft.keepass.utils.MenuUtil;
|
||||||
|
|
||||||
public abstract class ListNodesActivity extends LockingActivity
|
public abstract class ListNodesActivity extends LockingActivity
|
||||||
implements AssignMasterKeyDialogFragment.AssignPasswordDialogListener,
|
implements AssignMasterKeyDialogFragment.AssignPasswordDialogListener,
|
||||||
NodeAdapter.OnNodeClickCallback,
|
NodeAdapter.NodeClickCallback,
|
||||||
SortDialogFragment.SortSelectionListener {
|
SortDialogFragment.SortSelectionListener {
|
||||||
|
|
||||||
protected PwGroup mCurrentGroup;
|
protected static final String GROUP_ID_KEY = "GROUP_ID_KEY";
|
||||||
protected NodeAdapter mAdapter;
|
|
||||||
|
protected static final String LIST_NODES_FRAGMENT_TAG = "LIST_NODES_FRAGMENT_TAG";
|
||||||
private SharedPreferences prefs;
|
protected ListNodesFragment listNodesFragment;
|
||||||
|
|
||||||
|
protected PwGroup mCurrentGroup;
|
||||||
|
protected TextView groupNameView;
|
||||||
|
|
||||||
protected AutofillHelper autofillHelper;
|
protected AutofillHelper autofillHelper;
|
||||||
|
|
||||||
@@ -83,18 +72,12 @@ public abstract class ListNodesActivity extends LockingActivity
|
|||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
|
||||||
|
|
||||||
invalidateOptionsMenu();
|
invalidateOptionsMenu();
|
||||||
|
|
||||||
// TODO Move in search
|
mCurrentGroup = retrieveCurrentGroup(savedInstanceState);
|
||||||
setContentView(R.layout.list_nodes);
|
|
||||||
|
|
||||||
mCurrentGroup = initCurrentGroup();
|
initializeListNodesFragment(mCurrentGroup);
|
||||||
|
|
||||||
mAdapter = new NodeAdapter(this);
|
|
||||||
addOptionsToAdapter(mAdapter);
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
autofillHelper = new AutofillHelper();
|
autofillHelper = new AutofillHelper();
|
||||||
@@ -102,42 +85,71 @@ public abstract class ListNodesActivity extends LockingActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract PwGroup initCurrentGroup();
|
protected abstract PwGroup retrieveCurrentGroup(@Nullable Bundle savedInstanceState);
|
||||||
|
|
||||||
protected abstract RecyclerView defineNodeList();
|
|
||||||
|
|
||||||
protected void addOptionsToAdapter(NodeAdapter nodeAdapter) {
|
|
||||||
mAdapter.setOnNodeClickListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
// Add elements to the list
|
// Refresh the title
|
||||||
mAdapter.rebuildList(mCurrentGroup);
|
assignToolbarElements();
|
||||||
assignListToNodeAdapter(defineNodeList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setGroupTitle() {
|
protected void initializeListNodesFragment(PwGroup currentGroup) {
|
||||||
if ( mCurrentGroup != null ) {
|
listNodesFragment = (ListNodesFragment) getSupportFragmentManager()
|
||||||
String name = mCurrentGroup.getName();
|
.findFragmentByTag(LIST_NODES_FRAGMENT_TAG);
|
||||||
TextView tv = findViewById(R.id.group_name);
|
if (listNodesFragment == null)
|
||||||
if ( name != null && name.length() > 0 ) {
|
listNodesFragment = ListNodesFragment.newInstance(currentGroup);
|
||||||
if ( tv != null ) {
|
}
|
||||||
tv.setText(name);
|
|
||||||
}
|
/**
|
||||||
} else {
|
* Attach the fragment's list of node.
|
||||||
if ( tv != null ) {
|
* <br />
|
||||||
tv.setText(getText(R.string.root));
|
* <strong>R.id.nodes_list_fragment_container</strong> must be the id of the container
|
||||||
}
|
*/
|
||||||
}
|
protected void attachFragmentToContentView() {
|
||||||
}
|
getSupportFragmentManager().beginTransaction().replace(
|
||||||
|
R.id.nodes_list_fragment_container,
|
||||||
|
listNodesFragment,
|
||||||
|
LIST_NODES_FRAGMENT_TAG)
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assignToolbarElements() {
|
||||||
|
if (mCurrentGroup != null) {
|
||||||
|
String title = mCurrentGroup.getName();
|
||||||
|
if (title != null && title.length() > 0) {
|
||||||
|
if (groupNameView != null) {
|
||||||
|
groupNameView.setText(title);
|
||||||
|
groupNameView.invalidate();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (groupNameView != null) {
|
||||||
|
groupNameView.setText(getText(R.string.root));
|
||||||
|
groupNameView.invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
super.onCreateOptionsMenu(menu);
|
||||||
|
|
||||||
|
MenuInflater inflater = getMenuInflater();
|
||||||
|
MenuUtil.contributionMenuInflater(inflater, menu);
|
||||||
|
inflater.inflate(R.menu.default_menu, menu);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void assignListToNodeAdapter(RecyclerView recyclerView) {
|
@Override
|
||||||
recyclerView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
recyclerView.setLayoutManager(new LinearLayoutManager(this));
|
switch ( item.getItemId() ) {
|
||||||
recyclerView.setAdapter(mAdapter);
|
default:
|
||||||
|
// Check the time lock before launching settings
|
||||||
|
MenuUtil.onDefaultMenuOptionsItemSelected(this, item, true);
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -150,7 +162,7 @@ public abstract class ListNodesActivity extends LockingActivity
|
|||||||
if (assistStructure != null) {
|
if (assistStructure != null) {
|
||||||
switch (node.getType()) {
|
switch (node.getType()) {
|
||||||
case GROUP:
|
case GROUP:
|
||||||
GroupActivity.launch(this, (PwGroup) node, assistStructure);
|
openGroup((PwGroup) node);
|
||||||
break;
|
break;
|
||||||
case ENTRY:
|
case ENTRY:
|
||||||
// Build response with the entry selected
|
// Build response with the entry selected
|
||||||
@@ -163,7 +175,7 @@ public abstract class ListNodesActivity extends LockingActivity
|
|||||||
if ( assistStructure == null ){
|
if ( assistStructure == null ){
|
||||||
switch (node.getType()) {
|
switch (node.getType()) {
|
||||||
case GROUP:
|
case GROUP:
|
||||||
GroupActivity.launch(this, (PwGroup) node);
|
openGroup((PwGroup) node);
|
||||||
break;
|
break;
|
||||||
case ENTRY:
|
case ENTRY:
|
||||||
EntryActivity.launch(this, (PwEntry) node);
|
EntryActivity.launch(this, (PwEntry) node);
|
||||||
@@ -172,69 +184,26 @@ public abstract class ListNodesActivity extends LockingActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected void openGroup(PwGroup group) {
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
// Check Timeout
|
||||||
super.onCreateOptionsMenu(menu);
|
if (checkTimeIsAllowedOrFinish(this)) {
|
||||||
|
startRecordTime(this);
|
||||||
MenuInflater inflater = getMenuInflater();
|
|
||||||
MenuUtil.contributionMenuInflater(inflater, menu);
|
|
||||||
inflater.inflate(R.menu.tree, menu);
|
|
||||||
inflater.inflate(R.menu.default_menu, menu);
|
|
||||||
|
|
||||||
return true;
|
ListNodesFragment newListNodeFragment = ListNodesFragment.newInstance(group.getId());
|
||||||
}
|
getSupportFragmentManager().beginTransaction()
|
||||||
|
.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left,
|
||||||
@Override
|
R.anim.slide_in_left, R.anim.slide_out_right)
|
||||||
public void onSortSelected(SortNodeEnum sortNodeEnum, boolean ascending, boolean groupsBefore, boolean recycleBinBottom) {
|
.replace(R.id.nodes_list_fragment_container,
|
||||||
// Toggle setting
|
newListNodeFragment,
|
||||||
Editor editor = prefs.edit();
|
LIST_NODES_FRAGMENT_TAG)
|
||||||
editor.putString(getString(R.string.sort_node_key), sortNodeEnum.name());
|
.addToBackStack(LIST_NODES_FRAGMENT_TAG)
|
||||||
editor.putBoolean(getString(R.string.sort_ascending_key), ascending);
|
.commit();
|
||||||
editor.putBoolean(getString(R.string.sort_group_before_key), groupsBefore);
|
listNodesFragment = newListNodeFragment;
|
||||||
editor.putBoolean(getString(R.string.sort_recycle_bin_bottom_key), recycleBinBottom);
|
mCurrentGroup = group;
|
||||||
editor.apply();
|
assignToolbarElements();
|
||||||
|
}
|
||||||
// Tell the adapter to refresh it's list
|
|
||||||
mAdapter.notifyChangeSort(sortNodeEnum, ascending, groupsBefore);
|
|
||||||
mAdapter.rebuildList(mCurrentGroup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
switch ( item.getItemId() ) {
|
|
||||||
|
|
||||||
case R.id.menu_sort:
|
|
||||||
SortDialogFragment sortDialogFragment;
|
|
||||||
|
|
||||||
PwDatabase database = App.getDB().getPwDatabase();
|
|
||||||
/*
|
|
||||||
// TODO Recycle bin bottom
|
|
||||||
if (database.isRecycleBinAvailable() && database.isRecycleBinEnabled()) {
|
|
||||||
sortDialogFragment =
|
|
||||||
SortDialogFragment.getInstance(
|
|
||||||
PrefsUtil.getListSort(this),
|
|
||||||
PrefsUtil.getAscendingSort(this),
|
|
||||||
PrefsUtil.getGroupsBeforeSort(this),
|
|
||||||
PrefsUtil.getRecycleBinBottomSort(this));
|
|
||||||
} else {
|
|
||||||
*/
|
|
||||||
sortDialogFragment =
|
|
||||||
SortDialogFragment.getInstance(
|
|
||||||
PreferencesUtil.getListSort(this),
|
|
||||||
PreferencesUtil.getAscendingSort(this),
|
|
||||||
PreferencesUtil.getGroupsBeforeSort(this));
|
|
||||||
//}
|
|
||||||
|
|
||||||
sortDialogFragment.show(getSupportFragmentManager(), "sortDialog");
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Check the time lock before launching settings
|
|
||||||
MenuUtil.onDefaultMenuOptionsItemSelected(this, item, true);
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAssignKeyDialogPositiveClick(
|
public void onAssignKeyDialogPositiveClick(
|
||||||
boolean masterPasswordChecked, String masterPassword,
|
boolean masterPasswordChecked, String masterPassword,
|
||||||
@@ -253,29 +222,16 @@ public abstract class ListNodesActivity extends LockingActivity
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSortSelected(SortNodeEnum sortNodeEnum, boolean ascending, boolean groupsBefore, boolean recycleBinBottom) {
|
||||||
|
if (listNodesFragment != null)
|
||||||
|
listNodesFragment.onSortSelected(sortNodeEnum, ascending, groupsBefore, recycleBinBottom);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
|
||||||
switch (requestCode) {
|
|
||||||
case EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE:
|
|
||||||
if (resultCode == EntryEditActivity.ADD_ENTRY_RESULT_CODE ||
|
|
||||||
resultCode == EntryEditActivity.UPDATE_ENTRY_RESULT_CODE) {
|
|
||||||
PwNode newNode = (PwNode) data.getSerializableExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY);
|
|
||||||
if (newNode != null) {
|
|
||||||
if (resultCode == EntryEditActivity.ADD_ENTRY_RESULT_CODE)
|
|
||||||
mAdapter.addNode(newNode);
|
|
||||||
if (resultCode == EntryEditActivity.UPDATE_ENTRY_RESULT_CODE) {
|
|
||||||
//mAdapter.updateLastNodeRegister(newNode);
|
|
||||||
mAdapter.rebuildList(mCurrentGroup);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.e(this.getClass().getName(), "New node can be retrieve in Activity Result");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data);
|
AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data);
|
||||||
}
|
}
|
||||||
@@ -300,83 +256,18 @@ public abstract class ListNodesActivity extends LockingActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AfterAddNode extends AfterActionNodeOnFinish {
|
@Override
|
||||||
AfterAddNode(Handler handler) {
|
public void onBackPressed() {
|
||||||
super(handler);
|
if (checkTimeIsAllowedOrFinish(this)) {
|
||||||
}
|
startRecordTime(this);
|
||||||
|
|
||||||
public void run(PwNode oldNode, PwNode newNode) {
|
super.onBackPressed();
|
||||||
super.run();
|
|
||||||
|
|
||||||
runOnUiThread(() -> {
|
listNodesFragment = (ListNodesFragment) getSupportFragmentManager().findFragmentByTag(LIST_NODES_FRAGMENT_TAG);
|
||||||
if (mSuccess) {
|
// to refresh fragment
|
||||||
mAdapter.addNode(newNode);
|
listNodesFragment.rebuildList();
|
||||||
} else {
|
mCurrentGroup = listNodesFragment.getMainGroup();
|
||||||
displayMessage(ListNodesActivity.this);
|
assignToolbarElements();
|
||||||
}
|
|
||||||
|
|
||||||
SaveDatabaseProgressTaskDialogFragment.stop(ListNodesActivity.this);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AfterUpdateNode extends AfterActionNodeOnFinish {
|
|
||||||
AfterUpdateNode(Handler handler) {
|
|
||||||
super(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run(PwNode oldNode, PwNode newNode) {
|
|
||||||
super.run();
|
|
||||||
|
|
||||||
runOnUiThread(() -> {
|
|
||||||
if (mSuccess) {
|
|
||||||
mAdapter.updateNode(oldNode, newNode);
|
|
||||||
} else {
|
|
||||||
displayMessage(ListNodesActivity.this);
|
|
||||||
}
|
|
||||||
|
|
||||||
SaveDatabaseProgressTaskDialogFragment.stop(ListNodesActivity.this);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AfterDeleteNode extends OnFinishRunnable {
|
|
||||||
private PwNode pwNode;
|
|
||||||
|
|
||||||
AfterDeleteNode(Handler handler, PwNode pwNode) {
|
|
||||||
super(handler);
|
|
||||||
this.pwNode = pwNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
super.run();
|
|
||||||
|
|
||||||
runOnUiThread(() -> {
|
|
||||||
if ( mSuccess) {
|
|
||||||
mAdapter.removeNode(pwNode);
|
|
||||||
PwGroup parent = pwNode.getParent();
|
|
||||||
Database db = App.getDB();
|
|
||||||
PwDatabase database = db.getPwDatabase();
|
|
||||||
if (db.isRecycleBinAvailable() &&
|
|
||||||
db.isRecycleBinEnabled()) {
|
|
||||||
PwGroup recycleBin = database.getRecycleBin();
|
|
||||||
// Add trash if it doesn't exists
|
|
||||||
if (parent.equals(recycleBin)
|
|
||||||
&& mCurrentGroup != null
|
|
||||||
&& mCurrentGroup.getParent() == null
|
|
||||||
&& !mCurrentGroup.equals(recycleBin)) {
|
|
||||||
mAdapter.addNode(parent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mHandler.post(new UIToastTask(ListNodesActivity.this, "Unrecoverable error: " + mMessage));
|
|
||||||
App.setShutdown();
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
SaveDatabaseProgressTaskDialogFragment.stop(ListNodesActivity.this);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,298 @@
|
|||||||
|
package com.kunzisoft.keepass.activities;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import com.kunzisoft.keepass.R;
|
||||||
|
import com.kunzisoft.keepass.adapters.NodeAdapter;
|
||||||
|
import com.kunzisoft.keepass.app.App;
|
||||||
|
import com.kunzisoft.keepass.database.Database;
|
||||||
|
import com.kunzisoft.keepass.database.PwDatabase;
|
||||||
|
import com.kunzisoft.keepass.database.PwGroup;
|
||||||
|
import com.kunzisoft.keepass.database.PwGroupId;
|
||||||
|
import com.kunzisoft.keepass.database.PwNode;
|
||||||
|
import com.kunzisoft.keepass.database.SortNodeEnum;
|
||||||
|
import com.kunzisoft.keepass.dialogs.SortDialogFragment;
|
||||||
|
import com.kunzisoft.keepass.settings.PreferencesUtil;
|
||||||
|
import com.kunzisoft.keepass.stylish.StylishFragment;
|
||||||
|
|
||||||
|
public class ListNodesFragment extends StylishFragment implements
|
||||||
|
SortDialogFragment.SortSelectionListener {
|
||||||
|
|
||||||
|
private static final String TAG = ListNodesFragment.class.getName();
|
||||||
|
|
||||||
|
private static final String GROUP_KEY = "GROUP_KEY";
|
||||||
|
private static final String GROUP_ID_KEY = "GROUP_ID_KEY";
|
||||||
|
|
||||||
|
private NodeAdapter.NodeClickCallback nodeClickCallback;
|
||||||
|
private NodeAdapter.NodeMenuListener nodeMenuListener;
|
||||||
|
private OnScrollListener onScrollListener;
|
||||||
|
|
||||||
|
private RecyclerView listView;
|
||||||
|
protected PwGroup mCurrentGroup;
|
||||||
|
protected NodeAdapter mAdapter;
|
||||||
|
|
||||||
|
// Preferences for sorting
|
||||||
|
private SharedPreferences prefs;
|
||||||
|
|
||||||
|
public static ListNodesFragment newInstance(PwGroup group) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
if (group != null) {
|
||||||
|
bundle.putSerializable(GROUP_KEY, group);
|
||||||
|
}
|
||||||
|
ListNodesFragment listNodesFragment = new ListNodesFragment();
|
||||||
|
listNodesFragment.setArguments(bundle);
|
||||||
|
return listNodesFragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ListNodesFragment newInstance(PwGroupId groupId) {
|
||||||
|
Bundle bundle=new Bundle();
|
||||||
|
if (groupId != null) {
|
||||||
|
bundle.putSerializable(GROUP_ID_KEY, groupId);
|
||||||
|
}
|
||||||
|
ListNodesFragment listNodesFragment = new ListNodesFragment();
|
||||||
|
listNodesFragment.setArguments(bundle);
|
||||||
|
return listNodesFragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
try {
|
||||||
|
nodeClickCallback = (NodeAdapter.NodeClickCallback) context;
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
// The activity doesn't implement the interface, throw exception
|
||||||
|
throw new ClassCastException(context.toString()
|
||||||
|
+ " must implement " + NodeAdapter.NodeClickCallback.class.getName());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
nodeMenuListener = (NodeAdapter.NodeMenuListener) context;
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
nodeMenuListener = null;
|
||||||
|
// Context menu can be omit
|
||||||
|
Log.w(TAG, context.toString()
|
||||||
|
+ " must implement " + NodeAdapter.NodeMenuListener.class.getName());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
onScrollListener = (OnScrollListener) context;
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
onScrollListener = null;
|
||||||
|
// Context menu can be omit
|
||||||
|
Log.w(TAG, context.toString()
|
||||||
|
+ " must implement " + RecyclerView.OnScrollListener.class.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
|
||||||
|
mCurrentGroup = initCurrentGroup();
|
||||||
|
if (getActivity() != null) {
|
||||||
|
mAdapter = new NodeAdapter(getContextThemed(), getActivity().getMenuInflater());
|
||||||
|
mAdapter.setOnNodeClickListener(nodeClickCallback);
|
||||||
|
|
||||||
|
if (nodeMenuListener != null) {
|
||||||
|
mAdapter.setActivateContextMenu(true);
|
||||||
|
mAdapter.setNodeMenuListener(nodeMenuListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PwGroup initCurrentGroup() { // TODO Change by parcelable
|
||||||
|
|
||||||
|
Database db = App.getDB();
|
||||||
|
PwGroup root = db.getPwDatabase().getRootGroup();
|
||||||
|
|
||||||
|
PwGroup currentGroup = null;
|
||||||
|
if (getArguments() != null) {
|
||||||
|
// Contains all the group in element
|
||||||
|
if (getArguments().containsKey(GROUP_KEY)) {
|
||||||
|
currentGroup = (PwGroup) getArguments().getSerializable(GROUP_KEY);
|
||||||
|
}
|
||||||
|
// Contains only the group id, so the group must be retrieve
|
||||||
|
if (getArguments().containsKey(GROUP_ID_KEY)) {
|
||||||
|
PwGroupId pwGroupId = (PwGroupId) getArguments().getSerializable(GROUP_ID_KEY);
|
||||||
|
if ( pwGroupId != null )
|
||||||
|
currentGroup = db.getPwDatabase().getGroupByGroupId(pwGroupId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( currentGroup == null ) {
|
||||||
|
currentGroup = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreateView(inflater, container, savedInstanceState);
|
||||||
|
|
||||||
|
// To apply theme
|
||||||
|
View rootView = inflater.cloneInContext(getContextThemed())
|
||||||
|
.inflate(R.layout.list_nodes_fragment, container, false);
|
||||||
|
listView = rootView.findViewById(R.id.nodes_list);
|
||||||
|
|
||||||
|
if (onScrollListener != null) {
|
||||||
|
listView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||||
|
@Override
|
||||||
|
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||||
|
super.onScrolled(recyclerView, dx, dy);
|
||||||
|
onScrollListener.onScrolled(dy);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return rootView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
rebuildList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rebuildList() {
|
||||||
|
// Add elements to the list
|
||||||
|
mAdapter.rebuildList(mCurrentGroup);
|
||||||
|
assignListToNodeAdapter(listView);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assignListToNodeAdapter(RecyclerView recyclerView) {
|
||||||
|
recyclerView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
recyclerView.setAdapter(mAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSortSelected(SortNodeEnum sortNodeEnum, boolean ascending, boolean groupsBefore, boolean recycleBinBottom) {
|
||||||
|
// Toggle setting
|
||||||
|
SharedPreferences.Editor editor = prefs.edit();
|
||||||
|
editor.putString(getString(R.string.sort_node_key), sortNodeEnum.name());
|
||||||
|
editor.putBoolean(getString(R.string.sort_ascending_key), ascending);
|
||||||
|
editor.putBoolean(getString(R.string.sort_group_before_key), groupsBefore);
|
||||||
|
editor.putBoolean(getString(R.string.sort_recycle_bin_bottom_key), recycleBinBottom);
|
||||||
|
editor.apply();
|
||||||
|
|
||||||
|
// Tell the adapter to refresh it's list
|
||||||
|
mAdapter.notifyChangeSort(sortNodeEnum, ascending, groupsBefore);
|
||||||
|
mAdapter.rebuildList(mCurrentGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.tree, menu);
|
||||||
|
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch ( item.getItemId() ) {
|
||||||
|
|
||||||
|
case R.id.menu_sort:
|
||||||
|
SortDialogFragment sortDialogFragment;
|
||||||
|
|
||||||
|
PwDatabase database = App.getDB().getPwDatabase();
|
||||||
|
/*
|
||||||
|
// TODO Recycle bin bottom
|
||||||
|
if (database.isRecycleBinAvailable() && database.isRecycleBinEnabled()) {
|
||||||
|
sortDialogFragment =
|
||||||
|
SortDialogFragment.getInstance(
|
||||||
|
PrefsUtil.getListSort(this),
|
||||||
|
PrefsUtil.getAscendingSort(this),
|
||||||
|
PrefsUtil.getGroupsBeforeSort(this),
|
||||||
|
PrefsUtil.getRecycleBinBottomSort(this));
|
||||||
|
} else {
|
||||||
|
*/
|
||||||
|
sortDialogFragment =
|
||||||
|
SortDialogFragment.getInstance(
|
||||||
|
PreferencesUtil.getListSort(getContext()),
|
||||||
|
PreferencesUtil.getAscendingSort(getContext()),
|
||||||
|
PreferencesUtil.getGroupsBeforeSort(getContext()));
|
||||||
|
//}
|
||||||
|
|
||||||
|
sortDialogFragment.show(getChildFragmentManager(), "sortDialog");
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
|
||||||
|
switch (requestCode) {
|
||||||
|
case EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE:
|
||||||
|
if (resultCode == EntryEditActivity.ADD_ENTRY_RESULT_CODE ||
|
||||||
|
resultCode == EntryEditActivity.UPDATE_ENTRY_RESULT_CODE) {
|
||||||
|
PwNode newNode = (PwNode) data.getSerializableExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY);
|
||||||
|
if (newNode != null) {
|
||||||
|
if (resultCode == EntryEditActivity.ADD_ENTRY_RESULT_CODE)
|
||||||
|
mAdapter.addNode(newNode);
|
||||||
|
if (resultCode == EntryEditActivity.UPDATE_ENTRY_RESULT_CODE) {
|
||||||
|
//mAdapter.updateLastNodeRegister(newNode);
|
||||||
|
mAdapter.rebuildList(mCurrentGroup);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(this.getClass().getName(), "New node can be retrieve in Activity Result");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return mAdapter == null || mAdapter.getItemCount() <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNode(PwNode newNode) {
|
||||||
|
mAdapter.addNode(newNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateNode(PwNode oldNode, PwNode newNode) {
|
||||||
|
mAdapter.updateNode(oldNode, newNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeNode(PwNode pwNode) {
|
||||||
|
mAdapter.removeNode(pwNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PwGroup getMainGroup() {
|
||||||
|
return mCurrentGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnScrollListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback method to be invoked when the RecyclerView has been scrolled. This will be
|
||||||
|
* called after the scroll has completed.
|
||||||
|
*
|
||||||
|
* @param dy The amount of vertical scroll.
|
||||||
|
*/
|
||||||
|
void onScrolled(int dy);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,7 +44,12 @@ public abstract class LockingActivity extends StylishActivity {
|
|||||||
private ScreenReceiver screenReceiver;
|
private ScreenReceiver screenReceiver;
|
||||||
private boolean exitLock;
|
private boolean exitLock;
|
||||||
|
|
||||||
protected static void recordFirstTimeBeforeLaunch(Activity activity) {
|
|
||||||
|
/**
|
||||||
|
* Called to start a record time,
|
||||||
|
* Generally used for a first launch or for a fragment change
|
||||||
|
*/
|
||||||
|
protected static void startRecordTime(Activity activity) {
|
||||||
TimeoutHelper.recordTime(activity);
|
TimeoutHelper.recordTime(activity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,6 @@
|
|||||||
package com.kunzisoft.keepass.adapters;
|
package com.kunzisoft.keepass.adapters;
|
||||||
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import com.kunzisoft.keepass.R;
|
import com.kunzisoft.keepass.R;
|
||||||
|
|
||||||
@@ -30,7 +28,7 @@ class EntryViewHolder extends BasicViewHolder {
|
|||||||
EntryViewHolder(View itemView) {
|
EntryViewHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
container = itemView.findViewById(R.id.entry_container);
|
container = itemView.findViewById(R.id.entry_container);
|
||||||
icon = (ImageView) itemView.findViewById(R.id.entry_icon);
|
icon = itemView.findViewById(R.id.entry_icon);
|
||||||
text = (TextView) itemView.findViewById(R.id.entry_text);
|
text = itemView.findViewById(R.id.entry_text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,6 @@
|
|||||||
package com.kunzisoft.keepass.adapters;
|
package com.kunzisoft.keepass.adapters;
|
||||||
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import com.kunzisoft.keepass.R;
|
import com.kunzisoft.keepass.R;
|
||||||
|
|
||||||
@@ -30,7 +28,7 @@ class GroupViewHolder extends BasicViewHolder {
|
|||||||
GroupViewHolder(View itemView) {
|
GroupViewHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
container = itemView.findViewById(R.id.group_container);
|
container = itemView.findViewById(R.id.group_container);
|
||||||
icon = (ImageView) itemView.findViewById(R.id.group_icon);
|
icon = itemView.findViewById(R.id.group_icon);
|
||||||
text = (TextView) itemView.findViewById(R.id.group_text);
|
text = itemView.findViewById(R.id.group_text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import android.support.v7.widget.RecyclerView;
|
|||||||
import android.support.v7.widget.util.SortedListAdapterCallback;
|
import android.support.v7.widget.util.SortedListAdapterCallback;
|
||||||
import android.view.ContextMenu;
|
import android.view.ContextMenu;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -47,12 +47,13 @@ public class NodeAdapter extends RecyclerView.Adapter<BasicViewHolder> {
|
|||||||
|
|
||||||
private Context context;
|
private Context context;
|
||||||
private LayoutInflater inflater;
|
private LayoutInflater inflater;
|
||||||
|
private MenuInflater menuInflater;
|
||||||
private float textSize;
|
private float textSize;
|
||||||
private SortNodeEnum listSort;
|
private SortNodeEnum listSort;
|
||||||
private boolean groupsBeforeSort;
|
private boolean groupsBeforeSort;
|
||||||
private boolean ascendingSort;
|
private boolean ascendingSort;
|
||||||
|
|
||||||
private OnNodeClickCallback onNodeClickCallback;
|
private NodeClickCallback nodeClickCallback;
|
||||||
private NodeMenuListener nodeMenuListener;
|
private NodeMenuListener nodeMenuListener;
|
||||||
private boolean activateContextMenu;
|
private boolean activateContextMenu;
|
||||||
|
|
||||||
@@ -63,13 +64,11 @@ public class NodeAdapter extends RecyclerView.Adapter<BasicViewHolder> {
|
|||||||
* Create node list adapter with contextMenu or not
|
* Create node list adapter with contextMenu or not
|
||||||
* @param context Context to use
|
* @param context Context to use
|
||||||
*/
|
*/
|
||||||
public NodeAdapter(final Context context) {
|
public NodeAdapter(final Context context, MenuInflater menuInflater) {
|
||||||
this.inflater = LayoutInflater.from(context);
|
this.inflater = LayoutInflater.from(context);
|
||||||
|
this.menuInflater = menuInflater;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.textSize = PreferencesUtil.getListTextSize(context);
|
assignPreferences();
|
||||||
this.listSort = PreferencesUtil.getListSort(context);
|
|
||||||
this.groupsBeforeSort = PreferencesUtil.getGroupsBeforeSort(context);
|
|
||||||
this.ascendingSort = PreferencesUtil.getAscendingSort(context);
|
|
||||||
this.activateContextMenu = false;
|
this.activateContextMenu = false;
|
||||||
|
|
||||||
this.nodeSortedList = new SortedList<>(PwNode.class, new SortedListAdapterCallback<PwNode>(this) {
|
this.nodeSortedList = new SortedList<>(PwNode.class, new SortedListAdapterCallback<PwNode>(this) {
|
||||||
@@ -101,11 +100,19 @@ public class NodeAdapter extends RecyclerView.Adapter<BasicViewHolder> {
|
|||||||
this.activateContextMenu = activate;
|
this.activateContextMenu = activate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assignPreferences() {
|
||||||
|
this.textSize = PreferencesUtil.getListTextSize(context);
|
||||||
|
this.listSort = PreferencesUtil.getListSort(context);
|
||||||
|
this.groupsBeforeSort = PreferencesUtil.getGroupsBeforeSort(context);
|
||||||
|
this.ascendingSort = PreferencesUtil.getAscendingSort(context);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rebuild the list by clear and build children from the group
|
* Rebuild the list by clear and build children from the group
|
||||||
*/
|
*/
|
||||||
public void rebuildList(PwGroup group) {
|
public void rebuildList(PwGroup group) {
|
||||||
this.nodeSortedList.clear();
|
this.nodeSortedList.clear();
|
||||||
|
assignPreferences();
|
||||||
if (group != null) {
|
if (group != null) {
|
||||||
this.nodeSortedList.addAll(group.getDirectChildren());
|
this.nodeSortedList.addAll(group.getDirectChildren());
|
||||||
}
|
}
|
||||||
@@ -208,8 +215,8 @@ public class NodeAdapter extends RecyclerView.Adapter<BasicViewHolder> {
|
|||||||
/**
|
/**
|
||||||
* Assign a listener when a node is clicked
|
* Assign a listener when a node is clicked
|
||||||
*/
|
*/
|
||||||
public void setOnNodeClickListener(OnNodeClickCallback onNodeClickCallback) {
|
public void setOnNodeClickListener(NodeClickCallback nodeClickCallback) {
|
||||||
this.onNodeClickCallback = onNodeClickCallback;
|
this.nodeClickCallback = nodeClickCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -222,7 +229,7 @@ public class NodeAdapter extends RecyclerView.Adapter<BasicViewHolder> {
|
|||||||
/**
|
/**
|
||||||
* Callback listener to redefine to do an action when a node is click
|
* Callback listener to redefine to do an action when a node is click
|
||||||
*/
|
*/
|
||||||
public interface OnNodeClickCallback {
|
public interface NodeClickCallback {
|
||||||
void onNodeClick(PwNode node);
|
void onNodeClick(PwNode node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,6 +239,8 @@ public class NodeAdapter extends RecyclerView.Adapter<BasicViewHolder> {
|
|||||||
public interface NodeMenuListener {
|
public interface NodeMenuListener {
|
||||||
boolean onOpenMenuClick(PwNode node);
|
boolean onOpenMenuClick(PwNode node);
|
||||||
boolean onEditMenuClick(PwNode node);
|
boolean onEditMenuClick(PwNode node);
|
||||||
|
boolean onCopyMenuClick(PwNode node);
|
||||||
|
boolean onMoveMenuClick(PwNode node);
|
||||||
boolean onDeleteMenuClick(PwNode node);
|
boolean onDeleteMenuClick(PwNode node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,8 +256,8 @@ public class NodeAdapter extends RecyclerView.Adapter<BasicViewHolder> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (onNodeClickCallback != null)
|
if (nodeClickCallback != null)
|
||||||
onNodeClickCallback.onNodeClick(node);
|
nodeClickCallback.onNodeClick(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,10 +266,6 @@ public class NodeAdapter extends RecyclerView.Adapter<BasicViewHolder> {
|
|||||||
*/
|
*/
|
||||||
private class ContextMenuBuilder implements View.OnCreateContextMenuListener {
|
private class ContextMenuBuilder implements View.OnCreateContextMenuListener {
|
||||||
|
|
||||||
private static final int MENU_OPEN = Menu.FIRST;
|
|
||||||
private static final int MENU_EDIT = MENU_OPEN + 1;
|
|
||||||
private static final int MENU_DELETE = MENU_EDIT + 1;
|
|
||||||
|
|
||||||
private PwNode node;
|
private PwNode node;
|
||||||
private NodeMenuListener menuListener;
|
private NodeMenuListener menuListener;
|
||||||
|
|
||||||
@@ -271,15 +276,25 @@ public class NodeAdapter extends RecyclerView.Adapter<BasicViewHolder> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) {
|
public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) {
|
||||||
MenuItem clearMenu = contextMenu.add(Menu.NONE, MENU_OPEN, Menu.NONE, R.string.menu_open);
|
menuInflater.inflate(R.menu.node_menu, contextMenu);
|
||||||
clearMenu.setOnMenuItemClickListener(mOnMyActionClickListener);
|
|
||||||
|
MenuItem menuItem = contextMenu.findItem(R.id.menu_open);
|
||||||
|
menuItem.setOnMenuItemClickListener(mOnMyActionClickListener);
|
||||||
if (!App.getDB().isReadOnly() && !node.equals(App.getDB().getPwDatabase().getRecycleBin())) {
|
if (!App.getDB().isReadOnly() && !node.equals(App.getDB().getPwDatabase().getRecycleBin())) {
|
||||||
// Edition
|
// Edition
|
||||||
clearMenu = contextMenu.add(Menu.NONE, MENU_EDIT, Menu.NONE, R.string.menu_edit);
|
menuItem = contextMenu.findItem(R.id.menu_edit);
|
||||||
clearMenu.setOnMenuItemClickListener(mOnMyActionClickListener);
|
menuItem.setOnMenuItemClickListener(mOnMyActionClickListener);
|
||||||
|
// Copy (not for group)
|
||||||
|
if (node.getType().equals(PwNode.Type.ENTRY)) {
|
||||||
|
menuItem = contextMenu.findItem(R.id.menu_copy);
|
||||||
|
menuItem.setOnMenuItemClickListener(mOnMyActionClickListener);
|
||||||
|
}
|
||||||
|
// Move
|
||||||
|
menuItem = contextMenu.findItem(R.id.menu_move);
|
||||||
|
menuItem.setOnMenuItemClickListener(mOnMyActionClickListener);
|
||||||
// Deletion
|
// Deletion
|
||||||
clearMenu = contextMenu.add(Menu.NONE, MENU_DELETE, Menu.NONE, R.string.menu_delete);
|
menuItem = contextMenu.findItem(R.id.menu_delete);
|
||||||
clearMenu.setOnMenuItemClickListener(mOnMyActionClickListener);
|
menuItem.setOnMenuItemClickListener(mOnMyActionClickListener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,11 +304,15 @@ public class NodeAdapter extends RecyclerView.Adapter<BasicViewHolder> {
|
|||||||
if (menuListener == null)
|
if (menuListener == null)
|
||||||
return false;
|
return false;
|
||||||
switch ( item.getItemId() ) {
|
switch ( item.getItemId() ) {
|
||||||
case MENU_OPEN:
|
case R.id.menu_open:
|
||||||
return menuListener.onOpenMenuClick(node);
|
return menuListener.onOpenMenuClick(node);
|
||||||
case MENU_EDIT:
|
case R.id.menu_edit:
|
||||||
return menuListener.onEditMenuClick(node);
|
return menuListener.onEditMenuClick(node);
|
||||||
case MENU_DELETE:
|
case R.id.menu_copy:
|
||||||
|
return menuListener.onCopyMenuClick(node);
|
||||||
|
case R.id.menu_move:
|
||||||
|
return menuListener.onMoveMenuClick(node);
|
||||||
|
case R.id.menu_delete:
|
||||||
return menuListener.onDeleteMenuClick(node);
|
return menuListener.onDeleteMenuClick(node);
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ import java.io.OutputStream;
|
|||||||
import java.io.SyncFailedException;
|
import java.io.SyncFailedException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|
||||||
public class Database {
|
public class Database {
|
||||||
@@ -659,6 +662,44 @@ public class Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A duplicate entry with the same values, a new UUID,
|
||||||
|
* @param entryToCopy
|
||||||
|
* @param newParent
|
||||||
|
*/
|
||||||
|
public @Nullable PwEntry copyEntry(PwEntry entryToCopy, PwGroup newParent) {
|
||||||
|
try {
|
||||||
|
// TODO encapsulate
|
||||||
|
switch (getPwDatabase().getVersion()) {
|
||||||
|
case V3:
|
||||||
|
PwEntryV3 entryV3Copied = ((PwEntryV3) entryToCopy).clone();
|
||||||
|
entryV3Copied.setUUID(UUID.randomUUID());
|
||||||
|
entryV3Copied.setParent((PwGroupV3) newParent);
|
||||||
|
addEntryTo(entryV3Copied, newParent);
|
||||||
|
return entryV3Copied;
|
||||||
|
case V4:
|
||||||
|
PwEntryV4 entryV4Copied = ((PwEntryV4) entryToCopy).clone();
|
||||||
|
entryV4Copied.setUUID(UUID.randomUUID());
|
||||||
|
entryV4Copied.setParent((PwGroupV4) newParent);
|
||||||
|
addEntryTo(entryV4Copied, newParent);
|
||||||
|
return entryV4Copied;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "This version of PwEntry can't be updated", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void moveEntry(PwEntry entryToMove, PwGroup newParent) {
|
||||||
|
removeEntryFrom(entryToMove, entryToMove.parent);
|
||||||
|
addEntryTo(entryToMove, newParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void moveGroup(PwGroup groupToMove, PwGroup newParent) {
|
||||||
|
removeGroupFrom(groupToMove, groupToMove.parent);
|
||||||
|
addGroupTo(groupToMove, newParent);
|
||||||
|
}
|
||||||
|
|
||||||
public void deleteEntry(PwEntry entry) {
|
public void deleteEntry(PwEntry entry) {
|
||||||
try {
|
try {
|
||||||
switch (getPwDatabase().getVersion()) {
|
switch (getPwDatabase().getVersion()) {
|
||||||
|
|||||||
@@ -119,6 +119,13 @@ public abstract class PwNode<Parent extends PwGroup> implements ISmallTimeLogger
|
|||||||
parent = prt;
|
parent = prt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if parent is present (can be a root or a detach element)
|
||||||
|
*/
|
||||||
|
public boolean containsParent() {
|
||||||
|
return getParent() != null;
|
||||||
|
}
|
||||||
|
|
||||||
public PwDate getCreationTime() {
|
public PwDate getCreationTime() {
|
||||||
return creation;
|
return creation;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.kunzisoft.keepass.database.action;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.kunzisoft.keepass.database.Database;
|
||||||
|
|
||||||
|
public abstract class ActionDatabaseRunnable extends RunnableOnFinish {
|
||||||
|
|
||||||
|
protected Database mDb;
|
||||||
|
protected Context mContext;
|
||||||
|
protected boolean mDontSave;
|
||||||
|
|
||||||
|
public ActionDatabaseRunnable(Context context, Database db, OnFinishRunnable finish, boolean dontSave) {
|
||||||
|
super(finish);
|
||||||
|
|
||||||
|
this.mDb = db;
|
||||||
|
this.mContext = context;
|
||||||
|
this.mDontSave = dontSave;
|
||||||
|
this.mFinish = new AfterActionRunnable(finish);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Commit to disk
|
||||||
|
SaveDatabaseRunnable save = new SaveDatabaseRunnable(mContext, mDb, mFinish, mDontSave);
|
||||||
|
save.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected void onFinish(boolean success, String message);
|
||||||
|
|
||||||
|
private class AfterActionRunnable extends OnFinishRunnable {
|
||||||
|
|
||||||
|
AfterActionRunnable(OnFinishRunnable finish) {
|
||||||
|
super(finish);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
onFinish(mSuccess, mMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,77 +30,56 @@ import com.kunzisoft.keepass.utils.UriUtil;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
public class AssignPasswordInDBRunnable extends RunnableOnFinish {
|
public class AssignPasswordInDBRunnable extends ActionDatabaseRunnable {
|
||||||
|
|
||||||
private String mPassword;
|
private String mPassword;
|
||||||
private Uri mKeyfile;
|
private Uri mKeyfile;
|
||||||
private Database mDb;
|
private byte[] mBackupKey;
|
||||||
private boolean mDontSave;
|
|
||||||
private Context ctx;
|
|
||||||
|
|
||||||
public AssignPasswordInDBRunnable(Context ctx, Database db, String password, Uri keyfile, OnFinishRunnable finish) {
|
public AssignPasswordInDBRunnable(Context ctx, Database db, String password, Uri keyfile, OnFinishRunnable finish) {
|
||||||
this(ctx, db, password, keyfile, finish, false);
|
this(ctx, db, password, keyfile, finish, false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AssignPasswordInDBRunnable(Context ctx, Database db, String password, Uri keyfile, OnFinishRunnable finish, boolean dontSave) {
|
public AssignPasswordInDBRunnable(Context ctx, Database db, String password, Uri keyfile, OnFinishRunnable finish, boolean dontSave) {
|
||||||
super(finish);
|
super(ctx, db, finish, dontSave);
|
||||||
|
|
||||||
mDb = db;
|
this.mPassword = password;
|
||||||
mPassword = password;
|
this.mKeyfile = keyfile;
|
||||||
mKeyfile = keyfile;
|
|
||||||
mDontSave = dontSave;
|
|
||||||
this.ctx = ctx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
PwDatabase pm = mDb.getPwDatabase();
|
PwDatabase pm = mDb.getPwDatabase();
|
||||||
|
|
||||||
byte[] backupKey = new byte[pm.getMasterKey().length];
|
mBackupKey = new byte[pm.getMasterKey().length];
|
||||||
System.arraycopy(pm.getMasterKey(), 0, backupKey, 0, backupKey.length);
|
System.arraycopy(pm.getMasterKey(), 0, mBackupKey, 0, mBackupKey.length);
|
||||||
|
|
||||||
// Set key
|
// Set key
|
||||||
try {
|
try {
|
||||||
InputStream is = UriUtil.getUriInputStream(ctx, mKeyfile);
|
InputStream is = UriUtil.getUriInputStream(mContext, mKeyfile);
|
||||||
pm.retrieveMasterKey(mPassword, is);
|
pm.retrieveMasterKey(mPassword, is);
|
||||||
} catch (InvalidKeyFileException e) {
|
} catch (InvalidKeyFileException e) {
|
||||||
erase(backupKey);
|
erase(mBackupKey);
|
||||||
finish(false, e.getMessage());
|
finish(false, e.getMessage());
|
||||||
return;
|
return;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
erase(backupKey);
|
erase(mBackupKey);
|
||||||
finish(false, e.getMessage());
|
finish(false, e.getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save Database
|
// Save Database
|
||||||
mFinish = new AfterSave(backupKey, mFinish);
|
super.run();
|
||||||
SaveDBRunnable save = new SaveDBRunnable(ctx, mDb, mFinish, mDontSave);
|
|
||||||
save.run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AfterSave extends OnFinishRunnable {
|
|
||||||
private byte[] mBackup;
|
|
||||||
|
|
||||||
public AfterSave(byte[] backup, OnFinishRunnable finish) {
|
|
||||||
super(finish);
|
|
||||||
|
|
||||||
mBackup = backup;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
protected void onFinish(boolean success, String message) {
|
||||||
if ( ! mSuccess ) {
|
if (!success) {
|
||||||
// Erase the current master key
|
// Erase the current master key
|
||||||
erase(mDb.getPwDatabase().getMasterKey());
|
erase(mDb.getPwDatabase().getMasterKey());
|
||||||
mDb.getPwDatabase().setMasterKey(mBackup);
|
mDb.getPwDatabase().setMasterKey(mBackupKey);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
super.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Overwrite the array as soon as we don't need it to avoid keeping the extra data in memory
|
/** Overwrite the array as soon as we don't need it to avoid keeping the extra data in memory
|
||||||
* @param array
|
* @param array
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public class CreateDBRunnable extends RunnableOnFinish {
|
|||||||
App.clearShutdown();
|
App.clearShutdown();
|
||||||
|
|
||||||
// Commit changes
|
// Commit changes
|
||||||
SaveDBRunnable save = new SaveDBRunnable(ctx, db, mFinish, mDontSave);
|
SaveDatabaseRunnable save = new SaveDatabaseRunnable(ctx, db, mFinish, mDontSave);
|
||||||
mFinish = null;
|
mFinish = null;
|
||||||
save.run();
|
save.run();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,103 +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.database.action;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import com.kunzisoft.keepass.database.Database;
|
|
||||||
import com.kunzisoft.keepass.database.PwEntry;
|
|
||||||
import com.kunzisoft.keepass.database.PwGroup;
|
|
||||||
|
|
||||||
/** Task to delete entries
|
|
||||||
* @author bpellin
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class DeleteEntryRunnable extends RunnableOnFinish {
|
|
||||||
|
|
||||||
private Database mDb;
|
|
||||||
private PwEntry mEntry;
|
|
||||||
private boolean mDontSave;
|
|
||||||
private Context ctx;
|
|
||||||
|
|
||||||
public DeleteEntryRunnable(Context ctx, Database db, PwEntry entry, OnFinishRunnable finish) {
|
|
||||||
this(ctx, db, entry, finish, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DeleteEntryRunnable(Context ctx, Database db, PwEntry entry, OnFinishRunnable finish, boolean dontSave) {
|
|
||||||
super(finish);
|
|
||||||
|
|
||||||
this.mDb = db;
|
|
||||||
this.mEntry = entry;
|
|
||||||
this.mDontSave = dontSave;
|
|
||||||
this.ctx = ctx;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
PwGroup parent = mEntry.getParent();
|
|
||||||
|
|
||||||
// Remove Entry from parent
|
|
||||||
boolean recycle = mDb.canRecycle(mEntry);
|
|
||||||
if (recycle) {
|
|
||||||
mDb.recycle(mEntry);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mDb.deleteEntry(mEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save
|
|
||||||
mFinish = new AfterDelete(mFinish, parent, mEntry, recycle);
|
|
||||||
|
|
||||||
// Commit database
|
|
||||||
SaveDBRunnable save = new SaveDBRunnable(ctx, mDb, mFinish, mDontSave);
|
|
||||||
save.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class AfterDelete extends OnFinishRunnable {
|
|
||||||
|
|
||||||
private PwGroup mParent;
|
|
||||||
private PwEntry mEntry;
|
|
||||||
private boolean recycled;
|
|
||||||
|
|
||||||
AfterDelete(OnFinishRunnable finish, PwGroup parent, PwEntry entry, boolean r) {
|
|
||||||
super(finish);
|
|
||||||
|
|
||||||
mParent = parent;
|
|
||||||
mEntry = entry;
|
|
||||||
recycled = r;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if ( !mSuccess ) {
|
|
||||||
if (recycled) {
|
|
||||||
mDb.undoRecycle(mEntry, mParent);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mDb.undoDeleteEntry(mEntry, mParent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO Callback after delete entry
|
|
||||||
|
|
||||||
super.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,125 +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.database.action;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import com.kunzisoft.keepass.app.App;
|
|
||||||
import com.kunzisoft.keepass.database.Database;
|
|
||||||
import com.kunzisoft.keepass.database.PwEntry;
|
|
||||||
import com.kunzisoft.keepass.database.PwGroup;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class DeleteGroupRunnable extends RunnableOnFinish {
|
|
||||||
|
|
||||||
private Context mContext;
|
|
||||||
private Database mDb;
|
|
||||||
private PwGroup<PwGroup, PwGroup, PwEntry> mGroup;
|
|
||||||
private boolean mDontSave;
|
|
||||||
|
|
||||||
public DeleteGroupRunnable(Context ctx, Database db, PwGroup<PwGroup, PwGroup, PwEntry> group, OnFinishRunnable finish) {
|
|
||||||
super(finish);
|
|
||||||
setMembers(ctx, db, group, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DeleteGroupRunnable(Context ctx, Database db, PwGroup<PwGroup, PwGroup, PwEntry> group, OnFinishRunnable finish, boolean dontSave) {
|
|
||||||
super(finish);
|
|
||||||
setMembers(ctx, db, group, dontSave);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setMembers(Context ctx, Database db, PwGroup<PwGroup, PwGroup, PwEntry> group, boolean dontSave) {
|
|
||||||
mDb = db;
|
|
||||||
mGroup = group;
|
|
||||||
mContext = ctx;
|
|
||||||
mDontSave = dontSave;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
PwGroup parent = mGroup.getParent();
|
|
||||||
|
|
||||||
// Remove Group from parent
|
|
||||||
boolean recycle = mDb.canRecycle(mGroup);
|
|
||||||
if (recycle) {
|
|
||||||
mDb.recycle(mGroup);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// TODO tests
|
|
||||||
// Remove child entries
|
|
||||||
List<PwEntry> childEnt = new ArrayList<>(mGroup.getChildEntries()); // TODO new Methods
|
|
||||||
for ( int i = 0; i < childEnt.size(); i++ ) {
|
|
||||||
DeleteEntryRunnable task = new DeleteEntryRunnable(mContext, mDb, childEnt.get(i), null, true);
|
|
||||||
task.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove child groups
|
|
||||||
List<PwGroup> childGrp = new ArrayList<>(mGroup.getChildGroups());
|
|
||||||
for ( int i = 0; i < childGrp.size(); i++ ) {
|
|
||||||
DeleteGroupRunnable task = new DeleteGroupRunnable(mContext, mDb, childGrp.get(i), null, true);
|
|
||||||
task.run();
|
|
||||||
}
|
|
||||||
mDb.deleteGroup(mGroup);
|
|
||||||
|
|
||||||
// Remove from PwDatabaseV3
|
|
||||||
// TODO ENcapsulate
|
|
||||||
mDb.getPwDatabase().getGroups().remove(mGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save
|
|
||||||
mFinish = new AfterDelete(mFinish, parent, mGroup, recycle);
|
|
||||||
|
|
||||||
// Commit Database
|
|
||||||
SaveDBRunnable save = new SaveDBRunnable(mContext, mDb, mFinish, mDontSave);
|
|
||||||
save.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class AfterDelete extends OnFinishRunnable {
|
|
||||||
|
|
||||||
private PwGroup mParent;
|
|
||||||
private PwGroup mGroup;
|
|
||||||
private boolean recycled;
|
|
||||||
|
|
||||||
AfterDelete(OnFinishRunnable finish, PwGroup parent, PwGroup mGroup, boolean recycle) {
|
|
||||||
super(finish);
|
|
||||||
|
|
||||||
this.mParent = parent;
|
|
||||||
this.mGroup = mGroup;
|
|
||||||
this.recycled = recycle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run() {
|
|
||||||
if ( !mSuccess ) {
|
|
||||||
if (recycled) {
|
|
||||||
mDb.undoRecycle(mGroup, mParent);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Let's not bother recovering from a failure to save a deleted tree. It is too much work.
|
|
||||||
App.setShutdown();
|
|
||||||
// TODO TEST pm.undoDeleteGroup(mGroup, mParent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO Callback after delete group
|
|
||||||
|
|
||||||
super.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,6 +24,7 @@ import android.net.Uri;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
public class FileOnFinishRunnable extends OnFinishRunnable implements Serializable {
|
public class FileOnFinishRunnable extends OnFinishRunnable implements Serializable {
|
||||||
|
|
||||||
private Uri mFilename = null;
|
private Uri mFilename = null;
|
||||||
protected FileOnFinishRunnable mOnFinish;
|
protected FileOnFinishRunnable mOnFinish;
|
||||||
|
|
||||||
|
|||||||
@@ -76,11 +76,29 @@ public class OnFinishRunnable implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Move
|
/**
|
||||||
|
* ONLY to use in UIThread, typically in an Activity, Fragment or a Service
|
||||||
|
* @param ctx Context to show the message
|
||||||
|
*/
|
||||||
protected void displayMessage(Context ctx) {
|
protected void displayMessage(Context ctx) {
|
||||||
if ( mMessage != null && mMessage.length() > 0 ) {
|
if ( mMessage != null && mMessage.length() > 0 ) {
|
||||||
Toast.makeText(ctx, mMessage, Toast.LENGTH_LONG).show();
|
Toast.makeText(ctx, mMessage, Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return mSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSuccess(boolean mSuccess) {
|
||||||
|
this.mSuccess = mSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return mMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String mMessage) {
|
||||||
|
this.mMessage = mMessage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,13 +26,13 @@ import com.kunzisoft.keepass.database.exception.PwDbOutputException;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class SaveDBRunnable extends RunnableOnFinish {
|
public class SaveDatabaseRunnable extends RunnableOnFinish {
|
||||||
|
|
||||||
private Context mCtx;
|
private Context mCtx;
|
||||||
private Database mDb;
|
private Database mDb;
|
||||||
private boolean mDontSave;
|
private boolean mDontSave;
|
||||||
|
|
||||||
public SaveDBRunnable(Context ctx, Database db, OnFinishRunnable finish, boolean dontSave) {
|
public SaveDatabaseRunnable(Context ctx, Database db, OnFinishRunnable finish, boolean dontSave) {
|
||||||
super(finish);
|
super(finish);
|
||||||
|
|
||||||
this.mDb = db;
|
this.mDb = db;
|
||||||
@@ -40,7 +40,7 @@ public class SaveDBRunnable extends RunnableOnFinish {
|
|||||||
this.mCtx = ctx;
|
this.mCtx = ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SaveDBRunnable(Context ctx, Database db, OnFinishRunnable finish) {
|
public SaveDatabaseRunnable(Context ctx, Database db, OnFinishRunnable finish) {
|
||||||
this(ctx, db, finish, false);
|
this(ctx, db, finish, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.kunzisoft.keepass.database.action.node;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.kunzisoft.keepass.database.Database;
|
||||||
|
import com.kunzisoft.keepass.database.PwNode;
|
||||||
|
import com.kunzisoft.keepass.database.action.ActionDatabaseRunnable;
|
||||||
|
|
||||||
|
abstract class ActionNodeDatabaseRunnable extends ActionDatabaseRunnable {
|
||||||
|
|
||||||
|
private AfterActionNodeOnFinish callbackRunnable;
|
||||||
|
|
||||||
|
public ActionNodeDatabaseRunnable(Context context, Database db, AfterActionNodeOnFinish finish, boolean dontSave) {
|
||||||
|
super(context, db, finish, dontSave);
|
||||||
|
this.callbackRunnable = finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback method who return the node(s) modified after an action
|
||||||
|
* - Add : @param oldNode NULL, @param newNode CreatedNode
|
||||||
|
* - Copy : @param oldNode NodeToCopy, @param newNode NodeCopied
|
||||||
|
* - Delete : @param oldNode NodeToDelete, @param NULL
|
||||||
|
* - Move : @param oldNode NULL, @param NodeToMove
|
||||||
|
* - Update : @param oldNode NodeToUpdate, @param NodeUpdated
|
||||||
|
*/
|
||||||
|
protected void callbackNodeAction(boolean success, String message, PwNode oldNode, PwNode newNode) {
|
||||||
|
if (callbackRunnable != null) {
|
||||||
|
callbackRunnable.setSuccess(success);
|
||||||
|
callbackRunnable.setMessage(message);
|
||||||
|
callbackRunnable.run(oldNode, newNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,57 +17,40 @@
|
|||||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.database.action;
|
package com.kunzisoft.keepass.database.action.node;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.kunzisoft.keepass.database.Database;
|
import com.kunzisoft.keepass.database.Database;
|
||||||
import com.kunzisoft.keepass.database.PwEntry;
|
import com.kunzisoft.keepass.database.PwEntry;
|
||||||
|
|
||||||
public class AddEntryRunnable extends RunnableOnFinish {
|
public class AddEntryRunnable extends ActionNodeDatabaseRunnable {
|
||||||
|
|
||||||
protected Database mDb;
|
private PwEntry mNewEntry;
|
||||||
private PwEntry mEntry;
|
|
||||||
private Context ctx;
|
|
||||||
private boolean mDontSave;
|
|
||||||
|
|
||||||
public AddEntryRunnable(Context ctx, Database db, PwEntry entry, OnFinishRunnable finish) {
|
public AddEntryRunnable(Context ctx, Database db, PwEntry entryToAdd, AfterActionNodeOnFinish finish) {
|
||||||
this(ctx, db, entry, finish, false);
|
this(ctx, db, entryToAdd, finish, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AddEntryRunnable(Context ctx, Database db, PwEntry entry, OnFinishRunnable finish, boolean dontSave) {
|
public AddEntryRunnable(Context ctx, Database db, PwEntry entryToAdd, AfterActionNodeOnFinish finish, boolean dontSave) {
|
||||||
super(finish);
|
super(ctx, db, finish, dontSave);
|
||||||
|
|
||||||
this.mDb = db;
|
this.mNewEntry = entryToAdd;
|
||||||
this.mEntry = entry;
|
|
||||||
this.ctx = ctx;
|
|
||||||
this.mDontSave = dontSave;
|
|
||||||
|
|
||||||
this.mFinish = new AfterAdd(mFinish);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
mDb.addEntryTo(mEntry, mEntry.getParent());
|
mDb.addEntryTo(mNewEntry, mNewEntry.getParent());
|
||||||
|
|
||||||
// Commit to disk
|
// Commit to disk
|
||||||
SaveDBRunnable save = new SaveDBRunnable(ctx, mDb, mFinish, mDontSave);
|
super.run();
|
||||||
save.run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AfterAdd extends OnFinishRunnable {
|
|
||||||
|
|
||||||
AfterAdd(OnFinishRunnable finish) {
|
@Override
|
||||||
super(finish);
|
protected void onFinish(boolean success, String message) {
|
||||||
}
|
if ( !success ) {
|
||||||
|
mDb.removeEntryFrom(mNewEntry, mNewEntry.getParent());
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if ( !mSuccess ) {
|
|
||||||
mDb.removeEntryFrom(mEntry, mEntry.getParent());
|
|
||||||
}
|
|
||||||
// TODO if add entry callback
|
|
||||||
super.run();
|
|
||||||
}
|
}
|
||||||
|
callbackNodeAction(success, message, null, mNewEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,34 +17,25 @@
|
|||||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.database.action;
|
package com.kunzisoft.keepass.database.action.node;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.kunzisoft.keepass.database.Database;
|
import com.kunzisoft.keepass.database.Database;
|
||||||
import com.kunzisoft.keepass.database.PwGroup;
|
import com.kunzisoft.keepass.database.PwGroup;
|
||||||
|
|
||||||
public class AddGroupRunnable extends RunnableOnFinish {
|
public class AddGroupRunnable extends ActionNodeDatabaseRunnable {
|
||||||
|
|
||||||
protected Database mDb;
|
|
||||||
private PwGroup mNewGroup;
|
private PwGroup mNewGroup;
|
||||||
private Context ctx;
|
|
||||||
private boolean mDontSave;
|
|
||||||
|
|
||||||
public AddGroupRunnable(Context ctx, Database db, PwGroup newGroup, AfterActionNodeOnFinish afterAddNode) {
|
public AddGroupRunnable(Context ctx, Database db, PwGroup newGroup, AfterActionNodeOnFinish afterAddNode) {
|
||||||
this(ctx, db, newGroup, afterAddNode, false);
|
this(ctx, db, newGroup, afterAddNode, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AddGroupRunnable(Context ctx, Database db, PwGroup newGroup, AfterActionNodeOnFinish afterAddNode,
|
public AddGroupRunnable(Context ctx, Database db, PwGroup newGroup, AfterActionNodeOnFinish afterAddNode, boolean dontSave) {
|
||||||
boolean dontSave) {
|
super(ctx, db, afterAddNode, dontSave);
|
||||||
super(afterAddNode);
|
|
||||||
|
|
||||||
this.mDb = db;
|
|
||||||
this.mNewGroup = newGroup;
|
this.mNewGroup = newGroup;
|
||||||
this.mDontSave = dontSave;
|
|
||||||
this.ctx = ctx;
|
|
||||||
|
|
||||||
this.mFinish = new AfterAdd(mFinish);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -52,28 +43,14 @@ public class AddGroupRunnable extends RunnableOnFinish {
|
|||||||
mDb.addGroupTo(mNewGroup, mNewGroup.getParent());
|
mDb.addGroupTo(mNewGroup, mNewGroup.getParent());
|
||||||
|
|
||||||
// Commit to disk
|
// Commit to disk
|
||||||
SaveDBRunnable save = new SaveDBRunnable(ctx, mDb, mFinish, mDontSave);
|
super.run();
|
||||||
save.run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AfterAdd extends OnFinishRunnable {
|
|
||||||
|
|
||||||
AfterAdd(OnFinishRunnable finish) {
|
@Override
|
||||||
super(finish);
|
protected void onFinish(boolean success, String message) {
|
||||||
}
|
if ( !success ) {
|
||||||
|
mDb.removeGroupFrom(mNewGroup, mNewGroup.getParent());
|
||||||
@Override
|
}
|
||||||
public void run() {
|
callbackNodeAction(success, message, null, mNewGroup);
|
||||||
if ( !mSuccess ) {
|
|
||||||
mDb.removeGroupFrom(mNewGroup, mNewGroup.getParent());
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Better callback
|
|
||||||
AfterActionNodeOnFinish afterAddNode =
|
|
||||||
(AfterActionNodeOnFinish) super.mOnFinish;
|
|
||||||
afterAddNode.mSuccess = mSuccess;
|
|
||||||
afterAddNode.mMessage = mMessage;
|
|
||||||
afterAddNode.run(null, mNewGroup);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,16 +17,20 @@
|
|||||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.database.action;
|
package com.kunzisoft.keepass.database.action.node;
|
||||||
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
|
||||||
import com.kunzisoft.keepass.database.PwNode;
|
import com.kunzisoft.keepass.database.PwNode;
|
||||||
|
import com.kunzisoft.keepass.database.action.OnFinishRunnable;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public abstract class AfterActionNodeOnFinish extends OnFinishRunnable {
|
public abstract class AfterActionNodeOnFinish extends OnFinishRunnable {
|
||||||
public AfterActionNodeOnFinish(Handler handler) {
|
|
||||||
super(handler);
|
public AfterActionNodeOnFinish() {
|
||||||
|
super(new Handler());
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void run(PwNode oldNode, PwNode newNode);
|
public abstract void run(@Nullable PwNode oldNode, @Nullable PwNode newNode);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* 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 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.database.action.node;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.kunzisoft.keepass.database.Database;
|
||||||
|
import com.kunzisoft.keepass.database.PwEntry;
|
||||||
|
import com.kunzisoft.keepass.database.PwGroup;
|
||||||
|
|
||||||
|
public class CopyEntryRunnable extends ActionNodeDatabaseRunnable {
|
||||||
|
|
||||||
|
private static final String TAG = CopyEntryRunnable.class.getName();
|
||||||
|
|
||||||
|
private PwEntry mEntryToCopy;
|
||||||
|
private PwEntry mEntryCopied;
|
||||||
|
private PwGroup mNewParent;
|
||||||
|
|
||||||
|
public CopyEntryRunnable(Context context, Database db, PwEntry oldE, PwGroup newParent, AfterActionNodeOnFinish afterAddNode) {
|
||||||
|
this(context, db, oldE, newParent, afterAddNode, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CopyEntryRunnable(Context context, Database db, PwEntry oldE, PwGroup newParent, AfterActionNodeOnFinish afterAddNode, boolean dontSave) {
|
||||||
|
super(context, db, afterAddNode, dontSave);
|
||||||
|
|
||||||
|
this.mEntryToCopy = oldE;
|
||||||
|
this.mNewParent = newParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Update entry with new values
|
||||||
|
mEntryCopied = mDb.copyEntry(mEntryToCopy, mNewParent);
|
||||||
|
|
||||||
|
if (mEntryCopied != null) {
|
||||||
|
mEntryCopied.touch(true, true);
|
||||||
|
|
||||||
|
// Commit to disk
|
||||||
|
super.run();
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Unable to create a copy of the entry");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onFinish(boolean success, String message) {
|
||||||
|
if ( !success ) {
|
||||||
|
// If we fail to save, try to delete the copy
|
||||||
|
try {
|
||||||
|
mDb.deleteEntry(mEntryCopied);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.i(TAG, "Unable to delete the copied entry");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callbackNodeAction(success, message, mEntryToCopy, mEntryCopied);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* 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.database.action.node;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.kunzisoft.keepass.database.Database;
|
||||||
|
import com.kunzisoft.keepass.database.PwEntry;
|
||||||
|
import com.kunzisoft.keepass.database.PwGroup;
|
||||||
|
|
||||||
|
public class DeleteEntryRunnable extends ActionNodeDatabaseRunnable {
|
||||||
|
|
||||||
|
private PwEntry mEntryToDelete;
|
||||||
|
private PwGroup mParent;
|
||||||
|
private boolean mRecycle;
|
||||||
|
|
||||||
|
public DeleteEntryRunnable(Context ctx, Database db, PwEntry entry, AfterActionNodeOnFinish finish) {
|
||||||
|
this(ctx, db, entry, finish, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeleteEntryRunnable(Context ctx, Database db, PwEntry entry, AfterActionNodeOnFinish finish, boolean dontSave) {
|
||||||
|
super(ctx, db, finish, dontSave);
|
||||||
|
|
||||||
|
this.mEntryToDelete = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mParent = mEntryToDelete.getParent();
|
||||||
|
|
||||||
|
// Remove Entry from parent
|
||||||
|
mRecycle = mDb.canRecycle(mEntryToDelete);
|
||||||
|
if (mRecycle) {
|
||||||
|
mDb.recycle(mEntryToDelete);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mDb.deleteEntry(mEntryToDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit database
|
||||||
|
super.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onFinish(boolean success, String message) {
|
||||||
|
if ( !success ) {
|
||||||
|
if (mRecycle) {
|
||||||
|
mDb.undoRecycle(mEntryToDelete, mParent);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mDb.undoDeleteEntry(mEntryToDelete, mParent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callbackNodeAction(success, message, mEntryToDelete, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* 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.database.action.node;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.kunzisoft.keepass.app.App;
|
||||||
|
import com.kunzisoft.keepass.database.Database;
|
||||||
|
import com.kunzisoft.keepass.database.PwEntry;
|
||||||
|
import com.kunzisoft.keepass.database.PwGroup;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DeleteGroupRunnable extends ActionNodeDatabaseRunnable {
|
||||||
|
|
||||||
|
private PwGroup<PwGroup, PwGroup, PwEntry> mGroupToDelete;
|
||||||
|
private PwGroup mParent;
|
||||||
|
private boolean mRecycle;
|
||||||
|
|
||||||
|
public DeleteGroupRunnable(Context ctx, Database db, PwGroup<PwGroup, PwGroup, PwEntry> group, AfterActionNodeOnFinish finish) {
|
||||||
|
this(ctx, db, group, finish, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeleteGroupRunnable(Context ctx, Database db, PwGroup<PwGroup, PwGroup, PwEntry> group, AfterActionNodeOnFinish finish, boolean dontSave) {
|
||||||
|
super(ctx, db, finish, dontSave);
|
||||||
|
mGroupToDelete = group;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mParent = mGroupToDelete.getParent();
|
||||||
|
|
||||||
|
// Remove Group from parent
|
||||||
|
mRecycle = mDb.canRecycle(mGroupToDelete);
|
||||||
|
if (mRecycle) {
|
||||||
|
mDb.recycle(mGroupToDelete);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO tests
|
||||||
|
// Remove child entries
|
||||||
|
List<PwEntry> childEnt = new ArrayList<>(mGroupToDelete.getChildEntries()); // TODO new Methods
|
||||||
|
for ( int i = 0; i < childEnt.size(); i++ ) {
|
||||||
|
DeleteEntryRunnable task = new DeleteEntryRunnable(mContext, mDb, childEnt.get(i), null, true);
|
||||||
|
task.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove child groups
|
||||||
|
List<PwGroup> childGrp = new ArrayList<>(mGroupToDelete.getChildGroups());
|
||||||
|
for ( int i = 0; i < childGrp.size(); i++ ) {
|
||||||
|
DeleteGroupRunnable task = new DeleteGroupRunnable(mContext, mDb, childGrp.get(i), null, true);
|
||||||
|
task.run();
|
||||||
|
}
|
||||||
|
mDb.deleteGroup(mGroupToDelete);
|
||||||
|
|
||||||
|
// Remove from PwDatabaseV3
|
||||||
|
// TODO ENcapsulate
|
||||||
|
mDb.getPwDatabase().getGroups().remove(mGroupToDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit Database
|
||||||
|
super.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onFinish(boolean success, String message) {
|
||||||
|
if ( !success ) {
|
||||||
|
if (mRecycle) {
|
||||||
|
mDb.undoRecycle(mGroupToDelete, mParent);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Let's not bother recovering from a failure to save a deleted tree. It is too much work.
|
||||||
|
App.setShutdown();
|
||||||
|
// TODO TEST pm.undoDeleteGroup(mGroup, mParent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callbackNodeAction(success, message, mGroupToDelete, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* 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 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.database.action.node;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.kunzisoft.keepass.database.Database;
|
||||||
|
import com.kunzisoft.keepass.database.PwEntry;
|
||||||
|
import com.kunzisoft.keepass.database.PwGroup;
|
||||||
|
|
||||||
|
public class MoveEntryRunnable extends ActionNodeDatabaseRunnable {
|
||||||
|
|
||||||
|
private static final String TAG = MoveEntryRunnable.class.getName();
|
||||||
|
|
||||||
|
private PwEntry mEntryToMove;
|
||||||
|
private PwGroup mOldParent;
|
||||||
|
private PwGroup mNewParent;
|
||||||
|
|
||||||
|
public MoveEntryRunnable(Context context, Database db, PwEntry oldE, PwGroup newParent, AfterActionNodeOnFinish afterAddNode) {
|
||||||
|
this(context, db, oldE, newParent, afterAddNode, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MoveEntryRunnable(Context context, Database db, PwEntry oldE, PwGroup newParent, AfterActionNodeOnFinish afterAddNode, boolean dontSave) {
|
||||||
|
super(context, db, afterAddNode, dontSave);
|
||||||
|
|
||||||
|
this.mEntryToMove = oldE;
|
||||||
|
this.mNewParent = newParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Move entry in new parent
|
||||||
|
|
||||||
|
mOldParent = mEntryToMove.getParent();
|
||||||
|
mDb.moveEntry(mEntryToMove, mNewParent);
|
||||||
|
|
||||||
|
if (mEntryToMove != null) {
|
||||||
|
mEntryToMove.touch(true, true);
|
||||||
|
|
||||||
|
// Commit to disk
|
||||||
|
super.run();
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Unable to create a copy of the entry");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onFinish(boolean success, String message) {
|
||||||
|
if ( !success ) {
|
||||||
|
// If we fail to save, try to remove in the first place
|
||||||
|
try {
|
||||||
|
mDb.moveEntry(mEntryToMove, mOldParent);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.i(TAG, "Unable to replace the entry");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callbackNodeAction(success, message, null, mEntryToMove);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* 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 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.database.action.node;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.kunzisoft.keepass.R;
|
||||||
|
import com.kunzisoft.keepass.database.Database;
|
||||||
|
import com.kunzisoft.keepass.database.PwGroup;
|
||||||
|
|
||||||
|
public class MoveGroupRunnable extends ActionNodeDatabaseRunnable {
|
||||||
|
|
||||||
|
private static final String TAG = MoveGroupRunnable.class.getName();
|
||||||
|
|
||||||
|
private PwGroup mGroupToMove;
|
||||||
|
private PwGroup mOldParent;
|
||||||
|
private PwGroup mNewParent;
|
||||||
|
|
||||||
|
public MoveGroupRunnable(Context context, Database db, PwGroup groupToMove, PwGroup newParent, AfterActionNodeOnFinish afterAddNode) {
|
||||||
|
this(context, db, groupToMove, newParent, afterAddNode, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MoveGroupRunnable(Context context, Database db, PwGroup groupToMove, PwGroup newParent, AfterActionNodeOnFinish afterAddNode, boolean dontSave) {
|
||||||
|
super(context, db, afterAddNode, dontSave);
|
||||||
|
|
||||||
|
this.mGroupToMove = groupToMove;
|
||||||
|
this.mNewParent = newParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mOldParent = mGroupToMove.getParent();
|
||||||
|
// Move group in new parent if not in the current group
|
||||||
|
if (!mGroupToMove.equals(mNewParent)
|
||||||
|
&& !mNewParent.isContainedIn(mGroupToMove)) {
|
||||||
|
mDb.moveGroup(mGroupToMove, mNewParent);
|
||||||
|
|
||||||
|
if (mGroupToMove != null) {
|
||||||
|
mGroupToMove.touch(true, true);
|
||||||
|
|
||||||
|
// Commit to disk
|
||||||
|
super.run();
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Unable to create a copy of the group");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Only finish thread
|
||||||
|
mFinish.setResult(false);
|
||||||
|
String message = mContext.getString(R.string.error_move_folder_in_itself);
|
||||||
|
Log.e(TAG, message);
|
||||||
|
mFinish.setMessage(message);
|
||||||
|
mFinish.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onFinish(boolean success, String message) {
|
||||||
|
if ( !success ) {
|
||||||
|
// If we fail to save, try to move in the first place
|
||||||
|
try {
|
||||||
|
mDb.moveGroup(mGroupToMove, mOldParent);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.i(TAG, "Unable to replace the group");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callbackNodeAction(success, message, null, mGroupToMove);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,68 +17,47 @@
|
|||||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.database.action;
|
package com.kunzisoft.keepass.database.action.node;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.kunzisoft.keepass.database.Database;
|
import com.kunzisoft.keepass.database.Database;
|
||||||
import com.kunzisoft.keepass.database.PwEntry;
|
import com.kunzisoft.keepass.database.PwEntry;
|
||||||
|
|
||||||
public class UpdateEntryRunnable extends RunnableOnFinish {
|
public class UpdateEntryRunnable extends ActionNodeDatabaseRunnable {
|
||||||
|
|
||||||
private Database mDb;
|
private PwEntry mOldEntry;
|
||||||
private PwEntry mOldE;
|
private PwEntry mNewEntry;
|
||||||
private PwEntry mNewE;
|
private PwEntry mBackupEntry;
|
||||||
private Context ctx;
|
|
||||||
private boolean mDontSave;
|
|
||||||
|
|
||||||
public UpdateEntryRunnable(Context ctx, Database db, PwEntry oldE, PwEntry newE, OnFinishRunnable finish) {
|
public UpdateEntryRunnable(Context ctx, Database db, PwEntry oldE, PwEntry newE, AfterActionNodeOnFinish finish) {
|
||||||
this(ctx, db, oldE, newE, finish, false);
|
this(ctx, db, oldE, newE, finish, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UpdateEntryRunnable(Context ctx, Database db, PwEntry oldE, PwEntry newE, OnFinishRunnable finish, boolean dontSave) {
|
public UpdateEntryRunnable(Context ctx, Database db, PwEntry oldE, PwEntry newE, AfterActionNodeOnFinish finish, boolean dontSave) {
|
||||||
super(finish);
|
super(ctx, db, finish, dontSave);
|
||||||
|
|
||||||
this.mDb = db;
|
this.mOldEntry = oldE;
|
||||||
this.mOldE = oldE;
|
this.mNewEntry = newE;
|
||||||
this.mNewE = newE;
|
|
||||||
this.ctx = ctx;
|
|
||||||
this.mDontSave = dontSave;
|
|
||||||
|
|
||||||
// Keep backup of original values in case save fails
|
// Keep backup of original values in case save fails
|
||||||
PwEntry backup;
|
this.mBackupEntry = mOldEntry.clone();
|
||||||
backup = mOldE.clone();
|
|
||||||
|
|
||||||
mFinish = new AfterUpdate(backup, finish);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// Update entry with new values
|
// Update entry with new values
|
||||||
mDb.updateEntry(mOldE, mNewE);
|
mDb.updateEntry(mOldEntry, mNewEntry);
|
||||||
mOldE.touch(true, true);
|
mOldEntry.touch(true, true);
|
||||||
|
|
||||||
// Commit to disk
|
super.run();
|
||||||
SaveDBRunnable save = new SaveDBRunnable(ctx, mDb, mFinish, mDontSave);
|
|
||||||
save.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class AfterUpdate extends OnFinishRunnable {
|
|
||||||
private PwEntry mBackup;
|
|
||||||
|
|
||||||
AfterUpdate(PwEntry backup, OnFinishRunnable finish) {
|
|
||||||
super(finish);
|
|
||||||
mBackup = backup;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if ( !mSuccess ) {
|
|
||||||
// If we fail to save, back out changes to global structure
|
|
||||||
mDb.updateEntry(mOldE, mBackup);
|
|
||||||
}
|
|
||||||
// TODO Callback for update entry
|
|
||||||
super.run();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onFinish(boolean success, String message) {
|
||||||
|
if ( !success ) {
|
||||||
|
// If we fail to save, back out changes to global structure
|
||||||
|
mDb.updateEntry(mOldEntry, mBackupEntry);
|
||||||
|
}
|
||||||
|
callbackNodeAction(success, message, mOldEntry, mNewEntry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -17,39 +17,30 @@
|
|||||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.database.action;
|
package com.kunzisoft.keepass.database.action.node;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.kunzisoft.keepass.database.Database;
|
import com.kunzisoft.keepass.database.Database;
|
||||||
import com.kunzisoft.keepass.database.PwGroup;
|
import com.kunzisoft.keepass.database.PwGroup;
|
||||||
|
|
||||||
public class UpdateGroupRunnable extends RunnableOnFinish {
|
public class UpdateGroupRunnable extends ActionNodeDatabaseRunnable {
|
||||||
|
|
||||||
private Database mDb;
|
|
||||||
private PwGroup mOldGroup;
|
private PwGroup mOldGroup;
|
||||||
private PwGroup mNewGroup;
|
private PwGroup mNewGroup;
|
||||||
private Context ctx;
|
private PwGroup mBackupGroup;
|
||||||
private boolean mDontSave;
|
|
||||||
|
|
||||||
public UpdateGroupRunnable(Context ctx, Database db, PwGroup oldGroup, PwGroup newGroup, AfterActionNodeOnFinish finish) {
|
public UpdateGroupRunnable(Context ctx, Database db, PwGroup oldGroup, PwGroup newGroup, AfterActionNodeOnFinish finish) {
|
||||||
this(ctx, db, oldGroup, newGroup, finish, false);
|
this(ctx, db, oldGroup, newGroup, finish, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UpdateGroupRunnable(Context ctx, Database db, PwGroup oldGroup, PwGroup newGroup, AfterActionNodeOnFinish finish, boolean dontSave) {
|
public UpdateGroupRunnable(Context ctx, Database db, PwGroup oldGroup, PwGroup newGroup, AfterActionNodeOnFinish finish, boolean dontSave) {
|
||||||
super(finish);
|
super(ctx, db, finish, dontSave);
|
||||||
|
|
||||||
this.mDb = db;
|
|
||||||
this.mOldGroup = oldGroup;
|
this.mOldGroup = oldGroup;
|
||||||
this.mNewGroup = newGroup;
|
this.mNewGroup = newGroup;
|
||||||
this.ctx = ctx;
|
|
||||||
this.mDontSave = dontSave;
|
|
||||||
|
|
||||||
// Keep backup of original values in case save fails
|
// Keep backup of original values in case save fails
|
||||||
PwGroup backup;
|
this.mBackupGroup = mOldGroup.clone();
|
||||||
backup = mOldGroup.clone();
|
|
||||||
|
|
||||||
this.mFinish = new AfterUpdate(backup, finish);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -59,30 +50,15 @@ public class UpdateGroupRunnable extends RunnableOnFinish {
|
|||||||
mOldGroup.touch(true, true);
|
mOldGroup.touch(true, true);
|
||||||
|
|
||||||
// Commit to disk
|
// Commit to disk
|
||||||
new SaveDBRunnable(ctx, mDb, mFinish, mDontSave).run();
|
super.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AfterUpdate extends OnFinishRunnable {
|
|
||||||
private PwGroup mBackup;
|
|
||||||
|
|
||||||
AfterUpdate(PwGroup backup, OnFinishRunnable 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
|
@Override
|
||||||
AfterActionNodeOnFinish afterActionNodeOnFinish =
|
protected void onFinish(boolean success, String message) {
|
||||||
(AfterActionNodeOnFinish) super.mOnFinish;
|
if ( !success ) {
|
||||||
afterActionNodeOnFinish.mSuccess = mSuccess;
|
// If we fail to save, back out changes to global structure
|
||||||
afterActionNodeOnFinish.mMessage = mMessage;
|
mDb.updateGroup(mOldGroup, mBackupGroup);
|
||||||
afterActionNodeOnFinish.run(mOldGroup, mNewGroup);
|
|
||||||
}
|
}
|
||||||
|
callbackNodeAction(success, message, mOldGroup, mNewGroup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,6 @@ package com.kunzisoft.keepass.search;
|
|||||||
import android.app.SearchManager;
|
import android.app.SearchManager;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
@@ -36,9 +35,9 @@ import com.kunzisoft.keepass.database.Database;
|
|||||||
import com.kunzisoft.keepass.database.PwGroup;
|
import com.kunzisoft.keepass.database.PwGroup;
|
||||||
import com.kunzisoft.keepass.utils.MenuUtil;
|
import com.kunzisoft.keepass.utils.MenuUtil;
|
||||||
|
|
||||||
public class SearchResultsActivity extends ListNodesActivity {
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
private RecyclerView listView;
|
public class SearchResultsActivity extends ListNodesActivity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -52,23 +51,24 @@ public class SearchResultsActivity extends ListNodesActivity {
|
|||||||
assert getSupportActionBar() != null;
|
assert getSupportActionBar() != null;
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||||
|
groupNameView = findViewById(R.id.group_name);
|
||||||
|
|
||||||
|
attachFragmentToContentView();
|
||||||
|
|
||||||
listView = findViewById(R.id.nodes_list);
|
|
||||||
View notFoundView = findViewById(R.id.not_found_container);
|
View notFoundView = findViewById(R.id.not_found_container);
|
||||||
|
View listContainer = findViewById(R.id.nodes_list_fragment_container);
|
||||||
|
|
||||||
if ( mCurrentGroup == null || mCurrentGroup.numbersOfChildEntries() < 1 ) {
|
if ( mCurrentGroup == null || mCurrentGroup.numbersOfChildEntries() < 1 ) {
|
||||||
listView.setVisibility(View.GONE);
|
listContainer.setVisibility(View.GONE);
|
||||||
notFoundView.setVisibility(View.VISIBLE);
|
notFoundView.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
listView.setVisibility(View.VISIBLE);
|
listContainer.setVisibility(View.VISIBLE);
|
||||||
notFoundView.setVisibility(View.GONE);
|
notFoundView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
setGroupTitle();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PwGroup initCurrentGroup() {
|
protected PwGroup retrieveCurrentGroup(@Nullable Bundle savedInstanceState) {
|
||||||
Database mDb = App.getDB();
|
Database mDb = App.getDB();
|
||||||
// Likely the app has been killed exit the activity
|
// Likely the app has been killed exit the activity
|
||||||
if ( ! mDb.getLoaded() ) {
|
if ( ! mDb.getLoaded() ) {
|
||||||
@@ -77,17 +77,11 @@ public class SearchResultsActivity extends ListNodesActivity {
|
|||||||
return mDb.search(getSearchStr(getIntent()).trim());
|
return mDb.search(getSearchStr(getIntent()).trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected RecyclerView defineNodeList() {
|
|
||||||
return listView;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
|
||||||
MenuInflater inflater = getMenuInflater();
|
MenuInflater inflater = getMenuInflater();
|
||||||
MenuUtil.contributionMenuInflater(inflater, menu);
|
MenuUtil.contributionMenuInflater(inflater, menu);
|
||||||
inflater.inflate(R.menu.tree, menu);
|
|
||||||
inflater.inflate(R.menu.default_menu, menu);
|
inflater.inflate(R.menu.default_menu, menu);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -112,8 +112,8 @@ public class SettingsActivity extends LockingActivity implements MainPreferenceF
|
|||||||
@Override
|
@Override
|
||||||
public void onNestedPreferenceSelected(NestedSettingsFragment.Screen key) {
|
public void onNestedPreferenceSelected(NestedSettingsFragment.Screen key) {
|
||||||
getSupportFragmentManager().beginTransaction()
|
getSupportFragmentManager().beginTransaction()
|
||||||
.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left,
|
.setCustomAnimations(R.anim.slide_alpha_in_right, R.anim.slide_alpha_out_left,
|
||||||
R.anim.slide_in_left, R.anim.slide_out_right)
|
R.anim.slide_alpha_in_left, R.anim.slide_alpha_out_right)
|
||||||
.replace(R.id.fragment_container, NestedSettingsFragment.newInstance(key), TAG_NESTED)
|
.replace(R.id.fragment_container, NestedSettingsFragment.newInstance(key), TAG_NESTED)
|
||||||
.addToBackStack(TAG_NESTED)
|
.addToBackStack(TAG_NESTED)
|
||||||
.commit();
|
.commit();
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import android.view.View;
|
|||||||
import com.kunzisoft.keepass.app.App;
|
import com.kunzisoft.keepass.app.App;
|
||||||
import com.kunzisoft.keepass.database.Database;
|
import com.kunzisoft.keepass.database.Database;
|
||||||
import com.kunzisoft.keepass.database.action.OnFinishRunnable;
|
import com.kunzisoft.keepass.database.action.OnFinishRunnable;
|
||||||
import com.kunzisoft.keepass.database.action.SaveDBRunnable;
|
import com.kunzisoft.keepass.database.action.SaveDatabaseRunnable;
|
||||||
import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment;
|
import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment;
|
||||||
import com.kunzisoft.keepass.tasks.UpdateProgressTaskStatus;
|
import com.kunzisoft.keepass.tasks.UpdateProgressTaskStatus;
|
||||||
|
|
||||||
@@ -47,15 +47,15 @@ public abstract class DatabaseSavePreferenceDialogFragmentCompat extends InputP
|
|||||||
assert getActivity() != null;
|
assert getActivity() != null;
|
||||||
|
|
||||||
if (database != null && afterSaveDatabase != null) {
|
if (database != null && afterSaveDatabase != null) {
|
||||||
SaveDBRunnable saveDBRunnable = new SaveDBRunnable(getContext(),
|
SaveDatabaseRunnable saveDatabaseRunnable = new SaveDatabaseRunnable(getContext(),
|
||||||
database,
|
database,
|
||||||
afterSaveDatabase);
|
afterSaveDatabase);
|
||||||
saveDBRunnable.setUpdateProgressTaskStatus(
|
saveDatabaseRunnable.setUpdateProgressTaskStatus(
|
||||||
new UpdateProgressTaskStatus(getContext(),
|
new UpdateProgressTaskStatus(getContext(),
|
||||||
SaveDatabaseProgressTaskDialogFragment.start(
|
SaveDatabaseProgressTaskDialogFragment.start(
|
||||||
getActivity().getSupportFragmentManager())
|
getActivity().getSupportFragmentManager())
|
||||||
));
|
));
|
||||||
new Thread(saveDBRunnable).start();
|
new Thread(saveDatabaseRunnable).start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* 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 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.stylish;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.annotation.StyleRes;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v7.view.ContextThemeWrapper;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.Window;
|
||||||
|
|
||||||
|
public abstract class StylishFragment extends Fragment {
|
||||||
|
|
||||||
|
protected @StyleRes int themeId;
|
||||||
|
protected Context contextThemed; // TODO small ref
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
if (context != null) {
|
||||||
|
this.themeId = Stylish.getThemeId(context);
|
||||||
|
}
|
||||||
|
contextThemed = new ContextThemeWrapper(context, themeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
// To fix status bar color
|
||||||
|
if (getActivity() != null
|
||||||
|
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
Window window = getActivity().getWindow();
|
||||||
|
|
||||||
|
int[] attrColorPrimaryDark = {android.R.attr.colorPrimaryDark};
|
||||||
|
TypedArray taColorPrimaryDark = contextThemed.getTheme().obtainStyledAttributes(attrColorPrimaryDark);
|
||||||
|
window.setStatusBarColor(taColorPrimaryDark.getColor(0, Color.BLACK));
|
||||||
|
taColorPrimaryDark.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.onCreateView(inflater, container, savedInstanceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context getContextThemed() {
|
||||||
|
return contextThemed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,6 @@ import android.support.design.widget.FloatingActionButton;
|
|||||||
import android.support.v4.view.ViewCompat;
|
import android.support.v4.view.ViewCompat;
|
||||||
import android.support.v4.view.ViewPropertyAnimatorListener;
|
import android.support.v4.view.ViewPropertyAnimatorListener;
|
||||||
import android.support.v4.view.animation.FastOutSlowInInterpolator;
|
import android.support.v4.view.animation.FastOutSlowInInterpolator;
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
@@ -124,20 +123,14 @@ public class AddNodeButtonView extends RelativeLayout {
|
|||||||
return super.onTouchEvent(event);
|
return super.onTouchEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RecyclerView.OnScrollListener hideButtonOnScrollListener() {
|
public void hideButtonOnScrollListener(int dy) {
|
||||||
return new RecyclerView.OnScrollListener() {
|
if (state.equals(State.CLOSE)) {
|
||||||
@Override
|
if (dy > 0 && addButtonView.getVisibility() == View.VISIBLE) {
|
||||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
hideButton();
|
||||||
super.onScrolled(recyclerView, dx, dy);
|
} else if (dy < 0 && addButtonView.getVisibility() != View.VISIBLE) {
|
||||||
if (state.equals(State.CLOSE)) {
|
showButton();
|
||||||
if (dy > 0 && addButtonView.getVisibility() == View.VISIBLE) {
|
|
||||||
hideButton();
|
|
||||||
} else if (dy < 0 && addButtonView.getVisibility() != View.VISIBLE) {
|
|
||||||
showButton();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showButton() {
|
public void showButton() {
|
||||||
|
|||||||
26
app/src/main/res/anim/slide_alpha_in_left.xml
Normal file
26
app/src/main/res/anim/slide_alpha_in_left.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
/* //device/apps/common/res/anim/slide_in_left.xml
|
||||||
|
**
|
||||||
|
** Copyright 2007, The Android Open Source Project
|
||||||
|
**
|
||||||
|
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
** you may not use this file except in compliance with the License.
|
||||||
|
** You may obtain a copy of the License at
|
||||||
|
**
|
||||||
|
** http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
**
|
||||||
|
** Unless required by applicable law or agreed to in writing, software
|
||||||
|
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
** See the License for the specific language governing permissions and
|
||||||
|
** limitations under the License.
|
||||||
|
*/
|
||||||
|
-->
|
||||||
|
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<translate android:fromXDelta="-50%p" android:toXDelta="0"
|
||||||
|
android:duration="@android:integer/config_mediumAnimTime"/>
|
||||||
|
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
|
||||||
|
android:duration="@android:integer/config_mediumAnimTime" />
|
||||||
|
</set>
|
||||||
26
app/src/main/res/anim/slide_alpha_in_right.xml
Normal file
26
app/src/main/res/anim/slide_alpha_in_right.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
/* //device/apps/common/res/anim/slide_in_right.xml
|
||||||
|
**
|
||||||
|
** Copyright 2007, The Android Open Source Project
|
||||||
|
**
|
||||||
|
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
** you may not use this file except in compliance with the License.
|
||||||
|
** You may obtain a copy of the License at
|
||||||
|
**
|
||||||
|
** http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
**
|
||||||
|
** Unless required by applicable law or agreed to in writing, software
|
||||||
|
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
** See the License for the specific language governing permissions and
|
||||||
|
** limitations under the License.
|
||||||
|
*/
|
||||||
|
-->
|
||||||
|
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<translate android:fromXDelta="50%p" android:toXDelta="0"
|
||||||
|
android:duration="@android:integer/config_mediumAnimTime"/>
|
||||||
|
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
|
||||||
|
android:duration="@android:integer/config_mediumAnimTime" />
|
||||||
|
</set>
|
||||||
26
app/src/main/res/anim/slide_alpha_out_left.xml
Normal file
26
app/src/main/res/anim/slide_alpha_out_left.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
/* //device/apps/common/res/anim/slide_out_left.xml
|
||||||
|
**
|
||||||
|
** Copyright 2007, The Android Open Source Project
|
||||||
|
**
|
||||||
|
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
** you may not use this file except in compliance with the License.
|
||||||
|
** You may obtain a copy of the License at
|
||||||
|
**
|
||||||
|
** http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
**
|
||||||
|
** Unless required by applicable law or agreed to in writing, software
|
||||||
|
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
** See the License for the specific language governing permissions and
|
||||||
|
** limitations under the License.
|
||||||
|
*/
|
||||||
|
-->
|
||||||
|
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<translate android:fromXDelta="0" android:toXDelta="-50%p"
|
||||||
|
android:duration="@android:integer/config_mediumAnimTime"/>
|
||||||
|
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
|
||||||
|
android:duration="@android:integer/config_mediumAnimTime" />
|
||||||
|
</set>
|
||||||
26
app/src/main/res/anim/slide_alpha_out_right.xml
Normal file
26
app/src/main/res/anim/slide_alpha_out_right.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
/* //device/apps/common/res/anim/slide_out_right.xml
|
||||||
|
**
|
||||||
|
** Copyright 2007, The Android Open Source Project
|
||||||
|
**
|
||||||
|
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
** you may not use this file except in compliance with the License.
|
||||||
|
** You may obtain a copy of the License at
|
||||||
|
**
|
||||||
|
** http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
**
|
||||||
|
** Unless required by applicable law or agreed to in writing, software
|
||||||
|
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
** See the License for the specific language governing permissions and
|
||||||
|
** limitations under the License.
|
||||||
|
*/
|
||||||
|
-->
|
||||||
|
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<translate android:fromXDelta="0" android:toXDelta="50%p"
|
||||||
|
android:duration="@android:integer/config_mediumAnimTime"/>
|
||||||
|
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
|
||||||
|
android:duration="@android:integer/config_mediumAnimTime" />
|
||||||
|
</set>
|
||||||
@@ -19,8 +19,6 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<translate android:fromXDelta="-50%p" android:toXDelta="0"
|
<translate android:fromXDelta="-100%p" android:toXDelta="0"
|
||||||
android:duration="@android:integer/config_mediumAnimTime"/>
|
android:duration="@android:integer/config_mediumAnimTime"/>
|
||||||
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
|
|
||||||
android:duration="@android:integer/config_mediumAnimTime" />
|
|
||||||
</set>
|
</set>
|
||||||
|
|||||||
@@ -19,8 +19,6 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<translate android:fromXDelta="50%p" android:toXDelta="0"
|
<translate android:fromXDelta="100%p" android:toXDelta="0"
|
||||||
android:duration="@android:integer/config_mediumAnimTime"/>
|
android:duration="@android:integer/config_mediumAnimTime"/>
|
||||||
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
|
|
||||||
android:duration="@android:integer/config_mediumAnimTime" />
|
|
||||||
</set>
|
</set>
|
||||||
|
|||||||
@@ -19,8 +19,6 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<translate android:fromXDelta="0" android:toXDelta="-50%p"
|
<translate android:fromXDelta="0" android:toXDelta="-100%p"
|
||||||
android:duration="@android:integer/config_mediumAnimTime"/>
|
android:duration="@android:integer/config_mediumAnimTime"/>
|
||||||
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
|
|
||||||
android:duration="@android:integer/config_mediumAnimTime" />
|
|
||||||
</set>
|
</set>
|
||||||
|
|||||||
@@ -19,8 +19,6 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<translate android:fromXDelta="0" android:toXDelta="50%p"
|
<translate android:fromXDelta="0" android:toXDelta="100%p"
|
||||||
android:duration="@android:integer/config_mediumAnimTime"/>
|
android:duration="@android:integer/config_mediumAnimTime"/>
|
||||||
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
|
|
||||||
android:duration="@android:integer/config_mediumAnimTime" />
|
|
||||||
</set>
|
</set>
|
||||||
|
|||||||
9
app/src/main/res/drawable/ic_arrow_left_white_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_arrow_left_white_24dp.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M15.41,16.09l-4.58,-4.59 4.58,-4.59L14,5.5l-6,6 6,6z"/>
|
||||||
|
</vector>
|
||||||
@@ -4,6 +4,6 @@
|
|||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="#FF000000"
|
android:fillColor="#FFFFFFFF"
|
||||||
android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z"/>
|
android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z"/>
|
||||||
</vector>
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_content_cut_white_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_content_cut_white_24dp.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFFFF"
|
||||||
|
android:pathData="M9.64,7.64c0.23,-0.5 0.36,-1.05 0.36,-1.64 0,-2.21 -1.79,-4 -4,-4S2,3.79 2,6s1.79,4 4,4c0.59,0 1.14,-0.13 1.64,-0.36L10,12l-2.36,2.36C7.14,14.13 6.59,14 6,14c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4c0,-0.59 -0.13,-1.14 -0.36,-1.64L12,14l7,7h3v-1L9.64,7.64zM6,8c-1.1,0 -2,-0.89 -2,-2s0.9,-2 2,-2 2,0.89 2,2 -0.9,2 -2,2zM6,20c-1.1,0 -2,-0.89 -2,-2s0.9,-2 2,-2 2,0.89 2,2 -0.9,2 -2,2zM12,12.5c-0.28,0 -0.5,-0.22 -0.5,-0.5s0.22,-0.5 0.5,-0.5 0.5,0.22 0.5,0.5 -0.22,0.5 -0.5,0.5zM19,3l-6,6 2,2 7,-7L22,3z"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFFFF"
|
||||||
|
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||||
|
</vector>
|
||||||
@@ -43,6 +43,6 @@
|
|||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_alignParentRight="true"
|
android:layout_alignParentRight="true"
|
||||||
android:layout_below="@+id/title"
|
android:layout_below="@+id/title"
|
||||||
android:src="@drawable/ic_content_copy_black_24dp"
|
android:src="@drawable/ic_content_copy_white_24dp"
|
||||||
android:tint="?attr/colorAccent" />
|
android:tint="?attr/colorAccent" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_alignParentRight="true"
|
android:layout_alignParentRight="true"
|
||||||
android:layout_below="@+id/entry_user_name_label"
|
android:layout_below="@+id/entry_user_name_label"
|
||||||
android:src="@drawable/ic_content_copy_black_24dp"
|
android:src="@drawable/ic_content_copy_white_24dp"
|
||||||
android:tint="?attr/colorAccent" />
|
android:tint="?attr/colorAccent" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_alignParentRight="true"
|
android:layout_alignParentRight="true"
|
||||||
android:layout_below="@+id/entry_password_label"
|
android:layout_below="@+id/entry_password_label"
|
||||||
android:src="@drawable/ic_content_copy_black_24dp"
|
android:src="@drawable/ic_content_copy_white_24dp"
|
||||||
android:tint="?attr/colorAccent" />
|
android:tint="?attr/colorAccent" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,6 @@
|
|||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="@string/root"
|
android:text="@string/root"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:singleLine="true"
|
|
||||||
style="@style/KeepassDXStyle.TextAppearance.TitleTextOnPrimary" />
|
style="@style/KeepassDXStyle.TextAppearance.TitleTextOnPrimary" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
6
app/src/main/res/layout/list_nodes_fragment.xml
Normal file
6
app/src/main/res/layout/list_nodes_fragment.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/nodes_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?android:attr/windowBackground" />
|
||||||
@@ -17,63 +17,90 @@
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
<android.support.design.widget.CoordinatorLayout
|
<RelativeLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<android.support.design.widget.AppBarLayout
|
<android.support.design.widget.CoordinatorLayout
|
||||||
android:id="@+id/app_bar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:elevation="4dp"
|
|
||||||
android:fitsSystemWindows="true">
|
|
||||||
|
|
||||||
<android.support.design.widget.CollapsingToolbarLayout
|
|
||||||
android:id="@+id/toolbar_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:titleEnabled="false"
|
|
||||||
app:contentScrim="?attr/colorPrimary"
|
|
||||||
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
|
|
||||||
|
|
||||||
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:title="@string/app_name"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?attr/actionBarSize"
|
|
||||||
android:background="?attr/colorPrimary"
|
|
||||||
app:theme="?attr/toolbarAppearance"
|
|
||||||
app:popupTheme="?attr/toolbarPopupAppearance"
|
|
||||||
android:elevation="4dp"
|
|
||||||
tools:targetApi="lollipop">
|
|
||||||
<com.kunzisoft.keepass.view.GroupHeaderView
|
|
||||||
android:id="@+id/group_header"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentLeft="true"
|
|
||||||
android:layout_alignParentStart="true"
|
|
||||||
android:layout_below="@+id/toolbar" />
|
|
||||||
</android.support.v7.widget.Toolbar>
|
|
||||||
|
|
||||||
</android.support.design.widget.CollapsingToolbarLayout>
|
|
||||||
</android.support.design.widget.AppBarLayout>
|
|
||||||
|
|
||||||
<android.support.v7.widget.RecyclerView
|
|
||||||
android:id="@+id/nodes_list"
|
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_below="@+id/toolbar" />
|
android:layout_above="@+id/expandable_toolbar_paste_layout">
|
||||||
|
|
||||||
<com.kunzisoft.keepass.view.AddNodeButtonView
|
<android.support.design.widget.AppBarLayout
|
||||||
android:id="@+id/add_node_button"
|
android:id="@+id/app_bar"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:elevation="4dp"
|
||||||
|
android:fitsSystemWindows="true">
|
||||||
|
|
||||||
|
<android.support.design.widget.CollapsingToolbarLayout
|
||||||
|
android:id="@+id/toolbar_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:titleEnabled="false"
|
||||||
|
app:contentScrim="?attr/colorPrimary"
|
||||||
|
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
|
||||||
|
|
||||||
|
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:title="@string/app_name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
app:theme="?attr/toolbarAppearance"
|
||||||
|
app:popupTheme="?attr/toolbarPopupAppearance"
|
||||||
|
android:elevation="4dp"
|
||||||
|
tools:targetApi="lollipop">
|
||||||
|
<com.kunzisoft.keepass.view.GroupHeaderView
|
||||||
|
android:id="@+id/group_header"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_below="@+id/toolbar" />
|
||||||
|
</android.support.v7.widget.Toolbar>
|
||||||
|
|
||||||
|
</android.support.design.widget.CollapsingToolbarLayout>
|
||||||
|
</android.support.design.widget.AppBarLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/nodes_list_fragment_container"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_below="@+id/toolbar" />
|
||||||
|
|
||||||
|
<com.kunzisoft.keepass.view.AddNodeButtonView
|
||||||
|
android:id="@+id/add_node_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_anchor="@+id/nodes_list"
|
||||||
|
app:layout_anchorGravity="right|bottom" />
|
||||||
|
</android.support.design.widget.CoordinatorLayout>
|
||||||
|
|
||||||
|
<net.cachapa.expandablelayout.ExpandableLayout
|
||||||
|
android:id="@+id/expandable_toolbar_paste_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_anchor="@+id/nodes_list"
|
android:layout_alignParentBottom="true"
|
||||||
app:layout_anchorGravity="right|bottom" />
|
app:el_duration="300"
|
||||||
</android.support.design.widget.CoordinatorLayout>
|
app:el_expanded="false"
|
||||||
|
app:el_parallax="0.5">
|
||||||
|
|
||||||
|
<android.support.v7.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar_paste"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:elevation="4dp"
|
||||||
|
app:theme="?attr/toolbarBottomAppearance"
|
||||||
|
android:background="?attr/colorAccent"
|
||||||
|
tools:targetApi="lollipop" />
|
||||||
|
|
||||||
|
</net.cachapa.expandablelayout.ExpandableLayout>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
@@ -47,11 +47,10 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/no_results"/>
|
android:text="@string/no_results"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
<android.support.v7.widget.RecyclerView android:id="@+id/nodes_list"
|
<FrameLayout
|
||||||
|
android:id="@+id/nodes_list_fragment_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent" />
|
||||||
android:divider="@null"
|
|
||||||
android:dividerHeight="0dp"/>
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
47
app/src/main/res/menu/node_menu.xml
Normal file
47
app/src/main/res/menu/node_menu.xml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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 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/>.
|
||||||
|
-->
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item android:id="@+id/menu_open"
|
||||||
|
android:icon="@drawable/ic_launch_white_24dp"
|
||||||
|
android:title="@string/menu_open"
|
||||||
|
android:orderInCategory="1010"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
<item android:id="@+id/menu_edit"
|
||||||
|
android:icon="@drawable/ic_mode_edit_white_24dp"
|
||||||
|
android:title="@string/menu_edit"
|
||||||
|
android:orderInCategory="1020"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
<item android:id="@+id/menu_copy"
|
||||||
|
android:icon="@drawable/ic_content_copy_white_24dp"
|
||||||
|
android:title="@string/menu_copy"
|
||||||
|
android:orderInCategory="1040"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
<item android:id="@+id/menu_move"
|
||||||
|
android:icon="@drawable/ic_content_cut_white_24dp"
|
||||||
|
android:title="@string/menu_move"
|
||||||
|
android:orderInCategory="1030"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
<item android:id="@+id/menu_delete"
|
||||||
|
android:icon="@drawable/ic_content_delete_white_24dp"
|
||||||
|
android:title="@string/menu_delete"
|
||||||
|
android:orderInCategory="1050"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
</menu>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
Copyright 2018 Jeremy Jamet / Kunzisoft.
|
||||||
|
|
||||||
This file is part of KeePass DX.
|
This file is part of KeePass DX.
|
||||||
|
|
||||||
KeePass DX is free software: you can redistribute it and/or modify
|
KeePass DX is free software: you can redistribute it and/or modify
|
||||||
@@ -17,16 +17,10 @@
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
android:layout_height="match_parent">
|
<item android:id="@+id/menu_paste"
|
||||||
|
android:title="@string/menu_paste"
|
||||||
<include
|
android:orderInCategory="1090"
|
||||||
android:id="@+id/toolbar"
|
app:showAsAction="ifRoom|withText" />
|
||||||
layout="@layout/toolbar_default" />
|
</menu>
|
||||||
|
|
||||||
<android.support.v7.widget.RecyclerView android:id="@+id/nodes_list"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_below="@+id/toolbar" />
|
|
||||||
</RelativeLayout>
|
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
-->
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
<attr name="toolbarAppearance" format="reference" />
|
<attr name="toolbarAppearance" format="reference" />
|
||||||
|
<attr name="toolbarBottomAppearance" format="reference" />
|
||||||
<attr name="toolbarPopupAppearance" format="reference" />
|
<attr name="toolbarPopupAppearance" format="reference" />
|
||||||
|
|
||||||
<attr name="colorAccentCompat" format="reference|color" />
|
<attr name="colorAccentCompat" format="reference|color" />
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
<color name="transparent">#00ffffff</color>
|
<color name="transparent">#00ffffff</color>
|
||||||
<color name="white">#ffffff</color>
|
<color name="white">#ffffff</color>
|
||||||
<color name="black">#000000</color>
|
<color name="black">#000000</color>
|
||||||
|
<color name="dark">#0e0e0e</color>
|
||||||
|
|
||||||
<color name="orange_light">#ffa726</color>
|
<color name="orange_light">#ffa726</color>
|
||||||
<color name="orange">#fb8c00</color>
|
<color name="orange">#fb8c00</color>
|
||||||
|
|||||||
@@ -87,6 +87,7 @@
|
|||||||
<string name="error_title_required">A title is required.</string>
|
<string name="error_title_required">A title is required.</string>
|
||||||
<string name="error_wrong_length">Enter a positive integer on length field</string>
|
<string name="error_wrong_length">Enter a positive integer on length field</string>
|
||||||
<string name="error_autofill_enable_service">Autofill service can\'t be enabled.</string>
|
<string name="error_autofill_enable_service">Autofill service can\'t be enabled.</string>
|
||||||
|
<string name="error_move_folder_in_itself">Unable to move a group in itself.</string>
|
||||||
<string name="field_name">Field Name</string>
|
<string name="field_name">Field Name</string>
|
||||||
<string name="field_value">Field value</string>
|
<string name="field_value">Field value</string>
|
||||||
<string name="file_not_found">File not found.</string>
|
<string name="file_not_found">File not found.</string>
|
||||||
@@ -121,9 +122,13 @@
|
|||||||
<string name="menu_app_settings">Application settings</string>
|
<string name="menu_app_settings">Application settings</string>
|
||||||
<string name="menu_form_filling_settings">Form filling</string>
|
<string name="menu_form_filling_settings">Form filling</string>
|
||||||
<string name="menu_db_settings">Database settings</string>
|
<string name="menu_db_settings">Database settings</string>
|
||||||
<string name="menu_delete">Delete</string>
|
|
||||||
<string name="menu_donate">Donate</string>
|
<string name="menu_donate">Donate</string>
|
||||||
<string name="menu_edit">Edit</string>
|
<string name="menu_edit">Edit</string>
|
||||||
|
<string name="menu_copy">Copy</string>
|
||||||
|
<string name="menu_move">Move</string>
|
||||||
|
<string name="menu_paste">Paste</string>
|
||||||
|
<string name="menu_delete">Delete</string>
|
||||||
|
<string name="menu_cancel">Cancel</string>
|
||||||
<string name="menu_hide_password">Hide Pass</string>
|
<string name="menu_hide_password">Hide Pass</string>
|
||||||
<string name="menu_lock">Lock Database</string>
|
<string name="menu_lock">Lock Database</string>
|
||||||
<string name="menu_open">Open</string>
|
<string name="menu_open">Open</string>
|
||||||
|
|||||||
@@ -29,6 +29,8 @@
|
|||||||
<item name="android:textColorHintInverse">@color/blue_lighter</item>
|
<item name="android:textColorHintInverse">@color/blue_lighter</item>
|
||||||
<item name="android:windowBackground">@color/background_light</item>
|
<item name="android:windowBackground">@color/background_light</item>
|
||||||
<item name="toolbarAppearance">@style/KeepassDXStyle.Toolbar.Blue</item>
|
<item name="toolbarAppearance">@style/KeepassDXStyle.Toolbar.Blue</item>
|
||||||
|
<item name="toolbarBottomAppearance">@style/KeepassDXStyle.Toolbar.Bottom.Blue</item>
|
||||||
|
<item name="actionModeStyle">@style/KeepassDXStyle.ActionMode.Blue</item>
|
||||||
</style>
|
</style>
|
||||||
<!-- Toolbar Style Blue -->
|
<!-- Toolbar Style Blue -->
|
||||||
<style name="KeepassDXStyle.Toolbar.Blue" parent="KeepassDXStyle.Blue">
|
<style name="KeepassDXStyle.Toolbar.Blue" parent="KeepassDXStyle.Blue">
|
||||||
@@ -39,6 +41,13 @@
|
|||||||
<item name="android:editTextColor">@color/colorTextInverse</item>
|
<item name="android:editTextColor">@color/colorTextInverse</item>
|
||||||
<item name="android:textColorHint">@color/blue_lighter</item>
|
<item name="android:textColorHint">@color/blue_lighter</item>
|
||||||
</style>
|
</style>
|
||||||
|
<style name="KeepassDXStyle.Toolbar.Bottom.Blue" parent="KeepassDXStyle.Toolbar.Blue">
|
||||||
|
<item name="actionMenuTextColor">@color/colorTextInverse</item>
|
||||||
|
</style>
|
||||||
|
<!-- Contextual Action Bar Blue -->
|
||||||
|
<style name="KeepassDXStyle.ActionMode.Blue" parent="@style/Widget.AppCompat.ActionMode">
|
||||||
|
<item name="background">@color/blue_dark</item>
|
||||||
|
</style>
|
||||||
<!-- File Picker Theme -->
|
<!-- File Picker Theme -->
|
||||||
<style name="KeepassDXStyle.FilePickerStyle.Blue" parent="KeepassDXStyle.FilePickerStyle">
|
<style name="KeepassDXStyle.FilePickerStyle.Blue" parent="KeepassDXStyle.FilePickerStyle">
|
||||||
<item name="colorPrimary">@color/blue</item>
|
<item name="colorPrimary">@color/blue</item>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<!-- Dark Style -->
|
<!-- Dark Style -->
|
||||||
<style name="KeepassDXStyle.Dark" parent="KeepassDXStyle.Night.v21" >
|
<style name="KeepassDXStyle.Dark" parent="KeepassDXStyle.Night.v21" >
|
||||||
<item name="colorPrimary">#212121</item>
|
<item name="colorPrimary">#212121</item>
|
||||||
<item name="colorPrimaryDark">#0e0e0e</item>
|
<item name="colorPrimaryDark">@color/dark</item>
|
||||||
<item name="colorAccent">#26a69a</item>
|
<item name="colorAccent">#26a69a</item>
|
||||||
<item name="colorAccentCompat">#26a69a</item>
|
<item name="colorAccentCompat">#26a69a</item>
|
||||||
<item name="colorControlActivated">#80cbc4</item>
|
<item name="colorControlActivated">#80cbc4</item>
|
||||||
@@ -29,8 +29,10 @@
|
|||||||
<item name="android:textColorHintInverse">#80cbc4</item>
|
<item name="android:textColorHintInverse">#80cbc4</item>
|
||||||
<item name="android:windowBackground">@color/background_dark</item>
|
<item name="android:windowBackground">@color/background_dark</item>
|
||||||
<item name="toolbarAppearance">@style/KeepassDXStyle.Toolbar.Dark</item>
|
<item name="toolbarAppearance">@style/KeepassDXStyle.Toolbar.Dark</item>
|
||||||
|
<item name="toolbarBottomAppearance">@style/KeepassDXStyle.Toolbar.Dark</item>
|
||||||
<item name="android:alertDialogTheme">@style/KeepassDXStyle.Dark.Dialog</item>
|
<item name="android:alertDialogTheme">@style/KeepassDXStyle.Dark.Dialog</item>
|
||||||
<item name="alertDialogTheme">@style/KeepassDXStyle.Dark.Dialog</item>
|
<item name="alertDialogTheme">@style/KeepassDXStyle.Dark.Dialog</item>
|
||||||
|
<item name="actionModeStyle">@style/KeepassDXStyle.ActionMode.Dark</item>
|
||||||
</style>
|
</style>
|
||||||
<!-- Toolbar Style Dark -->
|
<!-- Toolbar Style Dark -->
|
||||||
<style name="KeepassDXStyle.Toolbar.Dark" parent="KeepassDXStyle.Dark">
|
<style name="KeepassDXStyle.Toolbar.Dark" parent="KeepassDXStyle.Dark">
|
||||||
@@ -38,6 +40,10 @@
|
|||||||
<item name="android:textColorPrimary">@color/colorTextInverse</item>
|
<item name="android:textColorPrimary">@color/colorTextInverse</item>
|
||||||
<item name="android:textColorSecondary">@color/colorTextSecondary</item>
|
<item name="android:textColorSecondary">@color/colorTextSecondary</item>
|
||||||
</style>
|
</style>
|
||||||
|
<!-- Contextual Action Bar Dark -->
|
||||||
|
<style name="KeepassDXStyle.ActionMode.Dark" parent="@style/Widget.AppCompat.ActionMode">
|
||||||
|
<item name="background">@color/dark</item>
|
||||||
|
</style>
|
||||||
<!-- Dialog -->
|
<!-- Dialog -->
|
||||||
<style name="KeepassDXStyle.Dark.Dialog" parent="KeepassDXStyle.Night.Dialog">
|
<style name="KeepassDXStyle.Dark.Dialog" parent="KeepassDXStyle.Night.Dialog">
|
||||||
<item name="colorAccent">#fefefe</item>
|
<item name="colorAccent">#fefefe</item>
|
||||||
|
|||||||
@@ -31,6 +31,8 @@
|
|||||||
<item name="android:textColorHintInverse">@color/purple_lighter</item>
|
<item name="android:textColorHintInverse">@color/purple_lighter</item>
|
||||||
<item name="android:windowBackground">@color/background_purple</item>
|
<item name="android:windowBackground">@color/background_purple</item>
|
||||||
<item name="toolbarAppearance">@style/KeepassDXStyle.Toolbar.Purple</item>
|
<item name="toolbarAppearance">@style/KeepassDXStyle.Toolbar.Purple</item>
|
||||||
|
<item name="toolbarBottomAppearance">@style/KeepassDXStyle.Toolbar.Bottom.Purple</item>
|
||||||
|
<item name="actionModeStyle">@style/KeepassDXStyle.ActionMode.Purple</item>
|
||||||
<item name="iconPreferenceColor">@color/purple_light</item>
|
<item name="iconPreferenceColor">@color/purple_light</item>
|
||||||
</style>
|
</style>
|
||||||
<!-- Toolbar Style Purple -->
|
<!-- Toolbar Style Purple -->
|
||||||
@@ -41,6 +43,13 @@
|
|||||||
<item name="android:editTextColor">@color/colorTextInverse</item>
|
<item name="android:editTextColor">@color/colorTextInverse</item>
|
||||||
<item name="android:textColorHint">@color/purple_lighter</item>
|
<item name="android:textColorHint">@color/purple_lighter</item>
|
||||||
</style>
|
</style>
|
||||||
|
<style name="KeepassDXStyle.Toolbar.Bottom.Purple" parent="KeepassDXStyle.Toolbar.Purple">
|
||||||
|
<item name="actionMenuTextColor">@color/colorTextInverse</item>
|
||||||
|
</style>
|
||||||
|
<!-- Contextual Action Bar Purple -->
|
||||||
|
<style name="KeepassDXStyle.ActionMode.Purple" parent="@style/Widget.AppCompat.ActionMode">
|
||||||
|
<item name="background">@color/purple_dark</item>
|
||||||
|
</style>
|
||||||
<!-- File Picker Theme -->
|
<!-- File Picker Theme -->
|
||||||
<style name="KeepassDXStyle.FilePickerStyle.Purple" parent="KeepassDXStyle.FilePickerStyle">
|
<style name="KeepassDXStyle.FilePickerStyle.Purple" parent="KeepassDXStyle.FilePickerStyle">
|
||||||
<item name="colorPrimary">@color/purple</item>
|
<item name="colorPrimary">@color/purple</item>
|
||||||
|
|||||||
@@ -29,6 +29,8 @@
|
|||||||
<item name="android:textColorHintInverse">@color/red_lighter</item>
|
<item name="android:textColorHintInverse">@color/red_lighter</item>
|
||||||
<item name="android:windowBackground">@color/background_night</item>
|
<item name="android:windowBackground">@color/background_night</item>
|
||||||
<item name="toolbarAppearance">@style/KeepassDXStyle.Toolbar.Red</item>
|
<item name="toolbarAppearance">@style/KeepassDXStyle.Toolbar.Red</item>
|
||||||
|
<item name="toolbarBottomAppearance">@style/KeepassDXStyle.Toolbar.Red</item>
|
||||||
|
<item name="actionModeStyle">@style/KeepassDXStyle.ActionMode.Red</item>
|
||||||
</style>
|
</style>
|
||||||
<!-- Toolbar Style Red -->
|
<!-- Toolbar Style Red -->
|
||||||
<style name="KeepassDXStyle.Toolbar.Red" parent="KeepassDXStyle.Red">
|
<style name="KeepassDXStyle.Toolbar.Red" parent="KeepassDXStyle.Red">
|
||||||
@@ -39,6 +41,10 @@
|
|||||||
<item name="android:editTextColor">@color/colorTextInverse</item>
|
<item name="android:editTextColor">@color/colorTextInverse</item>
|
||||||
<item name="android:textColorHint">@color/red_lighter</item>
|
<item name="android:textColorHint">@color/red_lighter</item>
|
||||||
</style>
|
</style>
|
||||||
|
<!-- Contextual Action Bar Red -->
|
||||||
|
<style name="KeepassDXStyle.ActionMode.Red" parent="@style/Widget.AppCompat.ActionMode">
|
||||||
|
<item name="background">@color/red_dark</item>
|
||||||
|
</style>
|
||||||
<!-- File Picker Theme -->
|
<!-- File Picker Theme -->
|
||||||
<style name="KeepassDXStyle.FilePickerStyle.Red" parent="KeepassDXStyle.FilePickerStyle">
|
<style name="KeepassDXStyle.FilePickerStyle.Red" parent="KeepassDXStyle.FilePickerStyle">
|
||||||
<item name="colorPrimary">@color/red</item>
|
<item name="colorPrimary">@color/red</item>
|
||||||
|
|||||||
@@ -29,9 +29,7 @@
|
|||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<!-- For setting encapsulation -->
|
<!-- For setting encapsulation -->
|
||||||
<style name="KeepassDXStyle.Light.v21" parent="Theme.AppCompat.Light">
|
<style name="KeepassDXStyle.Light.v21" parent="Theme.AppCompat.Light">
|
||||||
<!-- TODO Activate after navigation drawer
|
|
||||||
<item name="android:windowAnimationStyle">@style/KeepassDXStyle.ActivityAnimation</item>
|
<item name="android:windowAnimationStyle">@style/KeepassDXStyle.ActivityAnimation</item>
|
||||||
-->
|
|
||||||
|
|
||||||
<item name="windowNoTitle">true</item>
|
<item name="windowNoTitle">true</item>
|
||||||
<item name="windowActionBar">false</item>
|
<item name="windowActionBar">false</item>
|
||||||
@@ -73,14 +71,14 @@
|
|||||||
<!-- Toolbar -->
|
<!-- Toolbar -->
|
||||||
<item name="toolbarAppearance">@style/KeepassDXStyle.Toolbar.Light</item>
|
<item name="toolbarAppearance">@style/KeepassDXStyle.Toolbar.Light</item>
|
||||||
<item name="toolbarPopupAppearance">@style/KeepassDXStyle.Light.Toolbar.Popup</item>
|
<item name="toolbarPopupAppearance">@style/KeepassDXStyle.Light.Toolbar.Popup</item>
|
||||||
|
<item name="toolbarBottomAppearance">@style/KeepassDXStyle.Toolbar.Night</item>
|
||||||
|
<item name="actionModeStyle">@style/KeepassDXStyle.ActionMode</item>
|
||||||
|
|
||||||
<!-- White FAB -->
|
<!-- White FAB -->
|
||||||
<item name="whiteFab">@style/KeepassDXStyle.Fab.White</item>
|
<item name="whiteFab">@style/KeepassDXStyle.Fab.White</item>
|
||||||
</style>
|
</style>
|
||||||
<style name="KeepassDXStyle.Night.v21" parent="Theme.AppCompat">
|
<style name="KeepassDXStyle.Night.v21" parent="Theme.AppCompat">
|
||||||
<!--
|
|
||||||
<item name="android:windowAnimationStyle">@style/KeepassDXStyle.ActivityAnimation</item>
|
<item name="android:windowAnimationStyle">@style/KeepassDXStyle.ActivityAnimation</item>
|
||||||
-->
|
|
||||||
|
|
||||||
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
|
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
|
||||||
|
|
||||||
@@ -127,6 +125,8 @@
|
|||||||
<!-- Toolbar -->
|
<!-- Toolbar -->
|
||||||
<item name="toolbarAppearance">@style/KeepassDXStyle.Toolbar.Night</item>
|
<item name="toolbarAppearance">@style/KeepassDXStyle.Toolbar.Night</item>
|
||||||
<item name="toolbarPopupAppearance">@style/KeepassDXStyle.Night.Toolbar.Popup</item>
|
<item name="toolbarPopupAppearance">@style/KeepassDXStyle.Night.Toolbar.Popup</item>
|
||||||
|
<item name="toolbarBottomAppearance">@style/KeepassDXStyle.Toolbar.Night</item>
|
||||||
|
<item name="actionModeStyle">@style/KeepassDXStyle.ActionMode</item>
|
||||||
|
|
||||||
<!-- White FAB -->
|
<!-- White FAB -->
|
||||||
<item name="whiteFab">@style/KeepassDXStyle.Fab.White</item>
|
<item name="whiteFab">@style/KeepassDXStyle.Fab.White</item>
|
||||||
@@ -148,10 +148,10 @@
|
|||||||
|
|
||||||
<!-- Activity Animation -->
|
<!-- Activity Animation -->
|
||||||
<style name="KeepassDXStyle.ActivityAnimation" parent="@android:style/Animation.Activity">
|
<style name="KeepassDXStyle.ActivityAnimation" parent="@android:style/Animation.Activity">
|
||||||
<item name="android:activityOpenEnterAnimation">@anim/slide_in_right</item>
|
<item name="android:activityOpenEnterAnimation">@anim/slide_alpha_in_right</item>
|
||||||
<item name="android:activityOpenExitAnimation">@anim/slide_out_left</item>
|
<item name="android:activityOpenExitAnimation">@anim/slide_alpha_out_left</item>
|
||||||
<item name="android:activityCloseEnterAnimation">@anim/slide_in_left</item>
|
<item name="android:activityCloseEnterAnimation">@anim/slide_alpha_in_left</item>
|
||||||
<item name="android:activityCloseExitAnimation">@anim/slide_out_right</item>
|
<item name="android:activityCloseExitAnimation">@anim/slide_alpha_out_right</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- Toolbar Style Light Green -->
|
<!-- Toolbar Style Light Green -->
|
||||||
@@ -172,6 +172,11 @@
|
|||||||
<item name="android:textColorHint">@color/green_lighter</item>
|
<item name="android:textColorHint">@color/green_lighter</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<!-- Contextual Action Bar -->
|
||||||
|
<style name="KeepassDXStyle.ActionMode" parent="@style/Widget.AppCompat.ActionMode">
|
||||||
|
<item name="background">@color/green_dark</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<!-- Dialog -->
|
<!-- Dialog -->
|
||||||
<style name="KeepassDXStyle.Night.Dialog" parent="Theme.AppCompat.Dialog.Alert">
|
<style name="KeepassDXStyle.Night.Dialog" parent="Theme.AppCompat.Dialog.Alert">
|
||||||
<item name="colorAccent">@color/orange</item>
|
<item name="colorAccent">@color/orange</item>
|
||||||
|
|||||||
Reference in New Issue
Block a user