From 379263a6d3b32eca4ff746691f09fe26e6417a80 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Tue, 24 Jul 2018 14:48:03 +0200 Subject: [PATCH 1/7] Search and Group in one activity --- app/src/main/AndroidManifest.xml | 21 +- .../keepass/activities/GroupActivity.java | 443 ++++++++++++++---- .../keepass/activities/ListNodesActivity.java | 294 ------------ .../keepass/search/SearchResultsActivity.java | 114 ----- .../keepass/view/GroupHeaderView.java | 55 --- app/src/main/res/layout/group_header.xml | 52 -- .../res/layout/list_nodes_with_add_button.xml | 63 ++- 7 files changed, 421 insertions(+), 621 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/ListNodesActivity.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/search/SearchResultsActivity.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/view/GroupHeaderView.java delete mode 100644 app/src/main/res/layout/group_header.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0aca145fd..36986775d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -104,11 +104,19 @@ + android:windowSoftInputMode="adjustPan" + android:launchMode="singleTop"> + + + + + - - - - - - - diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index ac682ac09..2e154469d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -19,6 +19,7 @@ */ package com.kunzisoft.keepass.activities; +import android.annotation.SuppressLint; import android.app.Activity; import android.app.Dialog; import android.app.SearchManager; @@ -29,19 +30,23 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.res.TypedArray; import android.graphics.Color; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; +import android.support.v4.app.FragmentTransaction; import android.support.v7.widget.SearchView; import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.View; import android.widget.ImageView; +import android.widget.TextView; import com.getkeepsafe.taptargetview.TapTarget; import com.getkeepsafe.taptargetview.TapTargetView; @@ -57,6 +62,7 @@ import com.kunzisoft.keepass.database.PwGroupId; import com.kunzisoft.keepass.database.PwIcon; import com.kunzisoft.keepass.database.PwIconStandard; 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.node.AfterActionNodeOnFinish; import com.kunzisoft.keepass.database.action.node.CopyEntryRunnable; @@ -69,48 +75,67 @@ import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment; import com.kunzisoft.keepass.dialogs.GroupEditDialogFragment; import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment; import com.kunzisoft.keepass.dialogs.ReadOnlyDialog; +import com.kunzisoft.keepass.dialogs.SortDialogFragment; import com.kunzisoft.keepass.icons.IconPackChooser; -import com.kunzisoft.keepass.search.SearchResultsActivity; +import com.kunzisoft.keepass.lock.LockingActivity; +import com.kunzisoft.keepass.password.AssignPasswordHelper; import com.kunzisoft.keepass.selection.EntrySelectionHelper; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment; import com.kunzisoft.keepass.tasks.UIToastTask; import com.kunzisoft.keepass.tasks.UpdateProgressTaskStatus; +import com.kunzisoft.keepass.utils.MenuUtil; import com.kunzisoft.keepass.view.AddNodeButtonView; import net.cachapa.expandablelayout.ExpandableLayout; import static com.kunzisoft.keepass.activities.ReadOnlyHelper.READ_ONLY_DEFAULT; -public class GroupActivity extends ListNodesActivity +public class GroupActivity extends LockingActivity implements GroupEditDialogFragment.EditGroupListener, IconPickerDialogFragment.IconPickerListener, NodeAdapter.NodeMenuListener, - ListNodesFragment.OnScrollListener { + ListNodesFragment.OnScrollListener, + AssignMasterKeyDialogFragment.AssignPasswordDialogListener, + NodeAdapter.NodeClickCallback, + SortDialogFragment.SortSelectionListener { private static final String TAG = GroupActivity.class.getName(); - protected static final String GROUP_ID_KEY = "GROUP_ID_KEY"; + private static final String GROUP_ID_KEY = "GROUP_ID_KEY"; + private static final String LIST_NODES_FRAGMENT_TAG = "LIST_NODES_FRAGMENT_TAG"; + 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 Toolbar toolbar; - private ExpandableLayout toolbarPasteExpandableLayout; private Toolbar toolbarPaste; - private ImageView iconView; + private View readOnlyView; private AddNodeButtonView addNodeButtonView; + private TextView groupNameView; + private View notFoundView; + private View listContainer; - protected boolean addGroupEnabled = false; - protected boolean addEntryEnabled = false; - protected boolean isRoot = false; + private Database database; - 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 boolean addGroupEnabled = false; + private boolean addEntryEnabled = false; + private boolean isRoot = false; + + private ListNodesFragment listNodesFragment; + private boolean currentGroupIsASearch; + + private PwGroup rootGroup; + private PwGroup mCurrentGroup; private PwGroup oldGroupToUpdate; private PwNode nodeToCopy; private PwNode nodeToMove; + private boolean entrySelectionMode; + private AutofillHelper autofillHelper; + // After a database creation public static void launch(Activity act) { launch(act, READ_ONLY_DEFAULT); @@ -176,7 +201,38 @@ public class GroupActivity extends ListNodesActivity @Override protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + super.onCreate(savedInstanceState); + + if ( isFinishing() ) { + return; + } + + database = App.getDB(); + // Likely the app has been killed exit the activity + if ( ! database.getLoaded() ) { + finish(); + return; + } + + // Construct main view + setContentView(getLayoutInflater().inflate(R.layout.list_nodes_with_add_button, null)); + + // Initialize views + iconView = findViewById(R.id.icon); + readOnlyView = findViewById(R.id.read_only); + addNodeButtonView = findViewById(R.id.add_node_button); + toolbar = findViewById(R.id.toolbar); + groupNameView = findViewById(R.id.group_name); + toolbarPasteExpandableLayout = findViewById(R.id.expandable_toolbar_paste_layout); + toolbarPaste = findViewById(R.id.toolbar_paste); + notFoundView = findViewById(R.id.not_found_container); + listContainer = findViewById(R.id.nodes_list_fragment_container); + + invalidateOptionsMenu(); + + rootGroup = database.getPwDatabase().getRootGroup(); + mCurrentGroup = retrieveCurrentGroup(getIntent(), savedInstanceState); + currentGroupIsASearch = Intent.ACTION_SEARCH.equals(getIntent().getAction()); Log.i(TAG, "Started creating tree"); if ( mCurrentGroup == null ) { @@ -184,23 +240,12 @@ public class GroupActivity extends ListNodesActivity return; } - // Construct main view - setContentView(getLayoutInflater().inflate(R.layout.list_nodes_with_add_button, null)); - - attachFragmentToContentView(); - - iconView = findViewById(R.id.icon); - addNodeButtonView = findViewById(R.id.add_node_button); addNodeButtonView.enableAddGroup(addGroupEnabled); addNodeButtonView.enableAddEntry(addEntryEnabled); - toolbar = findViewById(R.id.toolbar); toolbar.setTitle(""); setSupportActionBar(toolbar); - groupNameView = findViewById(R.id.group_name); - toolbarPasteExpandableLayout = findViewById(R.id.expandable_toolbar_paste_layout); - 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 -> { @@ -209,6 +254,19 @@ public class GroupActivity extends ListNodesActivity nodeToMove = null; }); + // Initialize thr fragment with list + listNodesFragment = (ListNodesFragment) getSupportFragmentManager() + .findFragmentByTag(LIST_NODES_FRAGMENT_TAG); + if (listNodesFragment == null) + listNodesFragment = ListNodesFragment.newInstance(mCurrentGroup, readOnly); + + // Attach fragment to content view + getSupportFragmentManager().beginTransaction().replace( + R.id.nodes_list_fragment_container, + listNodesFragment, + LIST_NODES_FRAGMENT_TAG) + .commit(); + if (savedInstanceState != null) { if (savedInstanceState.containsKey(OLD_GROUP_TO_UPDATE_KEY)) oldGroupToUpdate = savedInstanceState.getParcelable(OLD_GROUP_TO_UPDATE_KEY); @@ -223,11 +281,9 @@ public class GroupActivity extends ListNodesActivity } } - addNodeButtonView.setAddGroupClickListener(v -> { - GroupEditDialogFragment.build() - .show(getSupportFragmentManager(), - GroupEditDialogFragment.TAG_CREATE_GROUP); - }); + addNodeButtonView.setAddGroupClickListener(v -> GroupEditDialogFragment.build() + .show(getSupportFragmentManager(), + GroupEditDialogFragment.TAG_CREATE_GROUP)); addNodeButtonView.setAddEntryClickListener(v -> EntryEditActivity.launch(GroupActivity.this, mCurrentGroup)); @@ -236,8 +292,40 @@ public class GroupActivity extends ListNodesActivity if (isRoot) { showWarnings(); } + + entrySelectionMode = EntrySelectionHelper.isIntentInEntrySelectionMode(getIntent()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + autofillHelper = new AutofillHelper(); + autofillHelper.retrieveAssistStructure(getIntent()); + } } + @Override + protected void onNewIntent(Intent intent) { + setIntent(intent); + if (Intent.ACTION_SEARCH.equals(intent.getAction())) { + // only one instance of search in backstack + openANewGroup(retrieveCurrentGroup(intent, null), !currentGroupIsASearch); + } + currentGroupIsASearch = Intent.ACTION_SEARCH.equals(getIntent().getAction()); + } + + private void openANewGroup(PwGroup group, boolean addToBackStack) { + ListNodesFragment newListNodeFragment = ListNodesFragment.newInstance(group, readOnly); + FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); + fragmentTransaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, + R.anim.slide_in_left, R.anim.slide_out_right) + .replace(R.id.nodes_list_fragment_container, + newListNodeFragment, + LIST_NODES_FRAGMENT_TAG); + if (addToBackStack) + fragmentTransaction.addToBackStack(LIST_NODES_FRAGMENT_TAG); + fragmentTransaction.commit(); + listNodesFragment = newListNodeFragment; + mCurrentGroup = group; + assignGroupViewElements(); + } + @Override protected void onSaveInstanceState(Bundle outState) { outState.putParcelable(GROUP_ID_KEY, mCurrentGroup.getId()); @@ -249,62 +337,117 @@ public class GroupActivity extends ListNodesActivity super.onSaveInstanceState(outState); } - protected PwGroup retrieveCurrentGroup(@Nullable Bundle savedInstanceState) { + protected PwGroup retrieveCurrentGroup(Intent intent, @Nullable Bundle savedInstanceState) { - PwGroupId pwGroupId = null; - if (savedInstanceState != null - && savedInstanceState.containsKey(GROUP_ID_KEY)) { - pwGroupId = savedInstanceState.getParcelable(GROUP_ID_KEY); - } else { - if (getIntent() != null) - pwGroupId = getIntent().getParcelableExtra(GROUP_ID_KEY); + // If it's a search + if ( Intent.ACTION_SEARCH.equals(intent.getAction()) ) { + return database.search(intent.getStringExtra(SearchManager.QUERY).trim()); } + // else a real group + else { + PwGroupId pwGroupId = null; + if (savedInstanceState != null + && savedInstanceState.containsKey(GROUP_ID_KEY)) { + pwGroupId = savedInstanceState.getParcelable(GROUP_ID_KEY); + } else { + if (getIntent() != null) + pwGroupId = intent.getParcelableExtra(GROUP_ID_KEY); + } - readOnly = database.isReadOnly() || readOnly; // Force read only if the database is like that + readOnly = database.isReadOnly() || readOnly; // Force read only if the database is like that - Log.w(TAG, "Creating tree view"); - PwGroup currentGroup; - if ( pwGroupId == null ) { - currentGroup = rootGroup; - } else { - currentGroup = database.getPwDatabase().getGroupByGroupId(pwGroupId); + Log.w(TAG, "Creating tree view"); + PwGroup currentGroup; + if (pwGroupId == null) { + currentGroup = rootGroup; + } else { + currentGroup = database.getPwDatabase().getGroupByGroupId(pwGroupId); + } + + if (currentGroup != null) { + addGroupEnabled = !readOnly; + addEntryEnabled = !readOnly; + isRoot = (currentGroup == rootGroup); + if (!currentGroup.allowAddEntryIfIsRoot()) + addEntryEnabled = !isRoot && addEntryEnabled; + } + + return currentGroup; } - - if (currentGroup != null) { - addGroupEnabled = !readOnly; - addEntryEnabled = !readOnly; - isRoot = (currentGroup == rootGroup); - if (!currentGroup.allowAddEntryIfIsRoot()) - addEntryEnabled = !isRoot && addEntryEnabled; - } - - return currentGroup; } - @Override - public void assignToolbarElements() { - 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); + public void assignGroupViewElements() { + // Assign title + if (mCurrentGroup != null) { + String title = mCurrentGroup.getName(); + if (title != null && title.length() > 0) { + if (groupNameView != null) { + groupNameView.setText(title); + groupNameView.invalidate(); + } } 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); + if (groupNameView != null) { + groupNameView.setText(getText(R.string.root)); + groupNameView.invalidate(); } } } + + // Assign read only text + if (readOnly) { + readOnlyView.setVisibility(View.VISIBLE); + } else { + readOnlyView.setVisibility(View.GONE); + } + + // Assign icon + if (currentGroupIsASearch) { + if (toolbar != null) { + toolbar.setNavigationIcon(null); + } + iconView.setVisibility(View.GONE); + } else { + // Assign the group icon depending of IconPack or custom icon + iconView.setVisibility(View.VISIBLE); + 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); + } + } + } + } + + // To show the " no search entry found " + if (currentGroupIsASearch && + (mCurrentGroup == null || mCurrentGroup.numbersOfChildEntries() < 1 )) { + listContainer.setVisibility(View.GONE); + notFoundView.setVisibility(View.VISIBLE); + } else { + listContainer.setVisibility(View.VISIBLE); + notFoundView.setVisibility(View.GONE); + } + + // Show button if allowed + if (currentGroupIsASearch) + addNodeButtonView.setVisibility(View.GONE); + else { + addNodeButtonView.setVisibility(View.VISIBLE); + if (addNodeButtonView != null) + addNodeButtonView.showButton(); + } } @Override @@ -313,6 +456,50 @@ public class GroupActivity extends ListNodesActivity addNodeButtonView.hideButtonOnScrollListener(dy); } + @Override + public void onNodeClick(PwNode node) { + + // Add event when we have Autofill + AssistStructure assistStructure = null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + assistStructure = autofillHelper.getAssistStructure(); + if (assistStructure != null) { + switch (node.getType()) { + case GROUP: + openGroup((PwGroup) node); + break; + case ENTRY: + // Build response with the entry selected + autofillHelper.buildResponseWhenEntrySelected(this, (PwEntry) node); + finish(); + break; + } + } + } + if ( assistStructure == null ){ + if (entrySelectionMode) { + switch (node.getType()) { + case GROUP: + openGroup((PwGroup) node); + break; + case ENTRY: + EntrySelectionHelper.buildResponseWhenEntrySelected(this, (PwEntry) node); + finish(); + break; + } + } else { + switch (node.getType()) { + case GROUP: + openGroup((PwGroup) node); + break; + case ENTRY: + EntryActivity.launch(this, (PwEntry) node, readOnly); + break; + } + } + } + } + @Override public boolean onOpenMenuClick(PwNode node) { onNodeClick(node); @@ -485,9 +672,8 @@ public class GroupActivity extends ListNodesActivity @Override protected void onResume() { super.onResume(); - // Show button on resume - if (addNodeButtonView != null) - addNodeButtonView.showButton(); + // Refresh the elements + assignGroupViewElements(); } /** @@ -652,11 +838,13 @@ public class GroupActivity extends ListNodesActivity searchView = (SearchView) searchItem.getActionView(); } if (searchView != null) { - // TODO Flickering when locking, will be better with content provider - searchView.setSearchableInfo(searchManager.getSearchableInfo(new ComponentName(this, SearchResultsActivity.class))); + searchView.setSearchableInfo(searchManager.getSearchableInfo(new ComponentName(this, GroupActivity.class))); searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default } + MenuUtil.contributionMenuInflater(inflater, menu); + inflater.inflate(R.menu.default_menu, menu); + super.onCreateOptionsMenu(menu); // Launch education screen @@ -673,7 +861,7 @@ public class GroupActivity extends ListNodesActivity if (Intent.ACTION_SEARCH.equals(intent.getAction())) { String query = intent.getStringExtra(SearchManager.QUERY); // manually launch the real search activity - final Intent searchIntent = new Intent(getApplicationContext(), SearchResultsActivity.class); + final Intent searchIntent = new Intent(getApplicationContext(), GroupActivity.class); // add query to the Intent Extras searchIntent.setAction(Intent.ACTION_SEARCH); searchIntent.putExtra(SearchManager.QUERY, query); @@ -706,7 +894,7 @@ public class GroupActivity extends ListNodesActivity return true; case R.id.menu_search: - onSearchRequested(); + //onSearchRequested(); return true; case R.id.menu_lock: @@ -716,8 +904,11 @@ public class GroupActivity extends ListNodesActivity case R.id.menu_change_master_key: setPassword(); return true; + default: + // Check the time lock before launching settings + MenuUtil.onDefaultMenuOptionsItemSelected(this, item, readOnly, true); + return super.onOptionsItemSelected(item); } - return super.onOptionsItemSelected(item); } private void setPassword() { @@ -789,6 +980,7 @@ public class GroupActivity extends ListNodesActivity class AfterAddNode extends AfterActionNodeOnFinish { + @Override public void run(PwNode oldNode, PwNode newNode) { super.run(); @@ -807,6 +999,7 @@ public class GroupActivity extends ListNodesActivity class AfterUpdateNode extends AfterActionNodeOnFinish { + @Override public void run(PwNode oldNode, PwNode newNode) { super.run(); @@ -891,17 +1084,89 @@ public class GroupActivity extends ListNodesActivity } } + private void openGroup(PwGroup group) { + // Check Timeout + if (checkTimeIsAllowedOrFinish(this)) { + startRecordTime(this); + // Open a new group and add the current one in the backstack + openANewGroup(group, true); + } + } + @Override - protected void openGroup(PwGroup group) { - super.openGroup(group); - if (addNodeButtonView != null) - addNodeButtonView.showButton(); + public void onAssignKeyDialogPositiveClick( + boolean masterPasswordChecked, String masterPassword, + boolean keyFileChecked, Uri keyFile) { + + AssignPasswordHelper assignPasswordHelper = + new AssignPasswordHelper(this, + masterPasswordChecked, masterPassword, keyFileChecked, keyFile); + assignPasswordHelper.assignPasswordInDatabase(null); + } + + @Override + public void onAssignKeyDialogNegativeClick( + boolean masterPasswordChecked, String masterPassword, + boolean keyFileChecked, Uri keyFile) { + + } + + @Override + public void onSortSelected(SortNodeEnum sortNodeEnum, boolean ascending, boolean groupsBefore, boolean recycleBinBottom) { + if (listNodesFragment != null) + listNodesFragment.onSortSelected(sortNodeEnum, ascending, groupsBefore, recycleBinBottom); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + EntrySelectionHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); + } + } + + @SuppressLint("RestrictedApi") + @Override + public void startActivityForResult(Intent intent, int requestCode, Bundle options) { + /* + * ACTION_SEARCH automatically forces a new task. This occurs when you open a kdb file in + * another app such as Files or GoogleDrive and then Search for an entry. Here we remove the + * FLAG_ACTIVITY_NEW_TASK flag bit allowing search to open it's activity in the current task. + */ + if (Intent.ACTION_SEARCH.equals(intent.getAction())) { + int flags = intent.getFlags(); + flags &= ~Intent.FLAG_ACTIVITY_NEW_TASK; + intent.setFlags(flags); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + super.startActivityForResult(intent, requestCode, options); + } + } + + private void removeSearchInIntent(Intent intent) { + if (Intent.ACTION_SEARCH.equals(intent.getAction())) { + currentGroupIsASearch = false; + intent.setAction(Intent.ACTION_DEFAULT); + intent.removeExtra(SearchManager.QUERY); + } } @Override public void onBackPressed() { - super.onBackPressed(); - if (addNodeButtonView != null) - addNodeButtonView.showButton(); + if (checkTimeIsAllowedOrFinish(this)) { + startRecordTime(this); + + super.onBackPressed(); + + listNodesFragment = (ListNodesFragment) getSupportFragmentManager().findFragmentByTag(LIST_NODES_FRAGMENT_TAG); + // to refresh fragment + listNodesFragment.rebuildList(); + mCurrentGroup = listNodesFragment.getMainGroup(); + removeSearchInIntent(getIntent()); + assignGroupViewElements(); + } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesActivity.java deleted file mode 100644 index 80d73bcdf..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesActivity.java +++ /dev/null @@ -1,294 +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 . - * - */ -package com.kunzisoft.keepass.activities; - -import android.annotation.SuppressLint; -import android.app.assist.AssistStructure; -import android.content.Intent; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.widget.TextView; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.adapters.NodeAdapter; -import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.autofill.AutofillHelper; -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwEntry; -import com.kunzisoft.keepass.database.PwGroup; -import com.kunzisoft.keepass.database.PwNode; -import com.kunzisoft.keepass.database.SortNodeEnum; -import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment; -import com.kunzisoft.keepass.dialogs.SortDialogFragment; -import com.kunzisoft.keepass.lock.LockingActivity; -import com.kunzisoft.keepass.password.AssignPasswordHelper; -import com.kunzisoft.keepass.selection.EntrySelectionHelper; -import com.kunzisoft.keepass.utils.MenuUtil; - -public abstract class ListNodesActivity extends LockingActivity - implements AssignMasterKeyDialogFragment.AssignPasswordDialogListener, - NodeAdapter.NodeClickCallback, - SortDialogFragment.SortSelectionListener { - - protected static final String LIST_NODES_FRAGMENT_TAG = "LIST_NODES_FRAGMENT_TAG"; - protected ListNodesFragment listNodesFragment; - - protected Database database; - protected PwGroup rootGroup; - protected PwGroup mCurrentGroup; - protected TextView groupNameView; - - protected boolean entrySelectionMode; - protected AutofillHelper autofillHelper; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - if ( isFinishing() ) { - return; - } - - database = App.getDB(); - - // Likely the app has been killed exit the activity - if ( ! database.getLoaded() ) { - finish(); - return; - } - - invalidateOptionsMenu(); - - rootGroup = database.getPwDatabase().getRootGroup(); - mCurrentGroup = retrieveCurrentGroup(savedInstanceState); - - initializeListNodesFragment(mCurrentGroup); - - entrySelectionMode = EntrySelectionHelper.isIntentInEntrySelectionMode(getIntent()); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - autofillHelper = new AutofillHelper(); - autofillHelper.retrieveAssistStructure(getIntent()); - } - } - - protected abstract PwGroup retrieveCurrentGroup(@Nullable Bundle savedInstanceState); - - @Override - protected void onResume() { - super.onResume(); - // Refresh the title - assignToolbarElements(); - } - - protected void initializeListNodesFragment(PwGroup currentGroup) { - listNodesFragment = (ListNodesFragment) getSupportFragmentManager() - .findFragmentByTag(LIST_NODES_FRAGMENT_TAG); - if (listNodesFragment == null) - listNodesFragment = ListNodesFragment.newInstance(currentGroup, readOnly); - } - - /** - * Attach the fragment's list of node. - *
- * R.id.nodes_list_fragment_container 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; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch ( item.getItemId() ) { - default: - // Check the time lock before launching settings - MenuUtil.onDefaultMenuOptionsItemSelected(this, item, readOnly, true); - return super.onOptionsItemSelected(item); - } - } - - @Override - public void onNodeClick(PwNode node) { - - // Add event when we have Autofill - AssistStructure assistStructure = null; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - assistStructure = autofillHelper.getAssistStructure(); - if (assistStructure != null) { - switch (node.getType()) { - case GROUP: - openGroup((PwGroup) node); - break; - case ENTRY: - // Build response with the entry selected - autofillHelper.buildResponseWhenEntrySelected(this, (PwEntry) node); - finish(); - break; - } - } - } - if ( assistStructure == null ){ - if (entrySelectionMode) { - switch (node.getType()) { - case GROUP: - openGroup((PwGroup) node); - break; - case ENTRY: - EntrySelectionHelper.buildResponseWhenEntrySelected(this, (PwEntry) node); - finish(); - break; - } - } else { - switch (node.getType()) { - case GROUP: - openGroup((PwGroup) node); - break; - case ENTRY: - EntryActivity.launch(this, (PwEntry) node, readOnly); - break; - } - } - } - } - - protected void openGroup(PwGroup group) { - // Check Timeout - if (checkTimeIsAllowedOrFinish(this)) { - startRecordTime(this); - - ListNodesFragment newListNodeFragment = ListNodesFragment.newInstance(group, readOnly); - getSupportFragmentManager().beginTransaction() - .setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, - R.anim.slide_in_left, R.anim.slide_out_right) - .replace(R.id.nodes_list_fragment_container, - newListNodeFragment, - LIST_NODES_FRAGMENT_TAG) - .addToBackStack(LIST_NODES_FRAGMENT_TAG) - .commit(); - listNodesFragment = newListNodeFragment; - mCurrentGroup = group; - assignToolbarElements(); - } - } - - @Override - public void onAssignKeyDialogPositiveClick( - boolean masterPasswordChecked, String masterPassword, - boolean keyFileChecked, Uri keyFile) { - - AssignPasswordHelper assignPasswordHelper = - new AssignPasswordHelper(this, - masterPasswordChecked, masterPassword, keyFileChecked, keyFile); - assignPasswordHelper.assignPasswordInDatabase(null); - } - - @Override - public void onAssignKeyDialogNegativeClick( - boolean masterPasswordChecked, String masterPassword, - boolean keyFileChecked, Uri keyFile) { - - } - - @Override - public void onSortSelected(SortNodeEnum sortNodeEnum, boolean ascending, boolean groupsBefore, boolean recycleBinBottom) { - if (listNodesFragment != null) - listNodesFragment.onSortSelected(sortNodeEnum, ascending, groupsBefore, recycleBinBottom); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - EntrySelectionHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); - } - } - - @SuppressLint("RestrictedApi") - @Override - public void startActivityForResult(Intent intent, int requestCode, Bundle options) { - /* - * ACTION_SEARCH automatically forces a new task. This occurs when you open a kdb file in - * another app such as Files or GoogleDrive and then Search for an entry. Here we remove the - * FLAG_ACTIVITY_NEW_TASK flag bit allowing search to open it's activity in the current task. - */ - if (Intent.ACTION_SEARCH.equals(intent.getAction())) { - int flags = intent.getFlags(); - flags &= ~Intent.FLAG_ACTIVITY_NEW_TASK; - intent.setFlags(flags); - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - super.startActivityForResult(intent, requestCode, options); - } - } - - @Override - public void onBackPressed() { - if (checkTimeIsAllowedOrFinish(this)) { - startRecordTime(this); - - super.onBackPressed(); - - listNodesFragment = (ListNodesFragment) getSupportFragmentManager().findFragmentByTag(LIST_NODES_FRAGMENT_TAG); - // to refresh fragment - listNodesFragment.rebuildList(); - mCurrentGroup = listNodesFragment.getMainGroup(); - assignToolbarElements(); - } - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/search/SearchResultsActivity.java b/app/src/main/java/com/kunzisoft/keepass/search/SearchResultsActivity.java deleted file mode 100644 index 3deb9e93c..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/search/SearchResultsActivity.java +++ /dev/null @@ -1,114 +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 . - * - */ -package com.kunzisoft.keepass.search; - -import android.app.SearchManager; -import android.content.Intent; -import android.os.Bundle; -import android.support.v7.widget.Toolbar; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.activities.ListNodesActivity; -import com.kunzisoft.keepass.activities.ListNodesFragment; -import com.kunzisoft.keepass.database.PwGroup; -import com.kunzisoft.keepass.utils.MenuUtil; - -import javax.annotation.Nullable; - -public class SearchResultsActivity extends ListNodesActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(getLayoutInflater().inflate(R.layout.search_results, null)); - - Toolbar toolbar = findViewById(R.id.toolbar); - toolbar.setTitle(getString(R.string.search_label)); - setSupportActionBar(toolbar); - assert getSupportActionBar() != null; - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setDisplayShowHomeEnabled(true); - groupNameView = findViewById(R.id.group_name); - - attachFragmentToContentView(); - - View notFoundView = findViewById(R.id.not_found_container); - View listContainer = findViewById(R.id.nodes_list_fragment_container); - - if ( mCurrentGroup == null || mCurrentGroup.numbersOfChildEntries() < 1 ) { - listContainer.setVisibility(View.GONE); - notFoundView.setVisibility(View.VISIBLE); - } else { - listContainer.setVisibility(View.VISIBLE); - notFoundView.setVisibility(View.GONE); - } - } - - @Override - protected void initializeListNodesFragment(PwGroup currentGroup) { - listNodesFragment = (ListNodesFragment) getSupportFragmentManager() - .findFragmentByTag(LIST_NODES_FRAGMENT_TAG); - // Directly get group and not id - if (listNodesFragment == null) - listNodesFragment = ListNodesFragment.newInstance(currentGroup, readOnly); - } - - @Override - protected PwGroup retrieveCurrentGroup(@Nullable Bundle savedInstanceState) { - // Likely the app has been killed exit the activity - if ( ! database.getLoaded() ) { - finish(); - } - return database.search(getSearchStr(getIntent()).trim()); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - - MenuInflater inflater = getMenuInflater(); - MenuUtil.contributionMenuInflater(inflater, menu); - inflater.inflate(R.menu.default_menu, menu); - - return true; - } - - public boolean onOptionsItemSelected(MenuItem item) { - switch ( item.getItemId() ) { - case android.R.id.home: - finish(); - } - return super.onOptionsItemSelected(item); - } - - private String getSearchStr(Intent queryIntent) { - // get and process search query here - final String queryAction = queryIntent.getAction(); - if ( Intent.ACTION_SEARCH.equals(queryAction) ) { - return queryIntent.getStringExtra(SearchManager.QUERY); - } - return ""; - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/view/GroupHeaderView.java b/app/src/main/java/com/kunzisoft/keepass/view/GroupHeaderView.java deleted file mode 100644 index c1bbb9e4b..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/view/GroupHeaderView.java +++ /dev/null @@ -1,55 +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 . - * - */ -package com.kunzisoft.keepass.view; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.RelativeLayout; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.app.App; - -public class GroupHeaderView extends RelativeLayout { - - public GroupHeaderView(Context context) { - this(context, null); - } - - public GroupHeaderView(Context context, AttributeSet attrs) { - super(context, attrs); - - inflate(context); - } - - private void inflate(Context context) { - LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - assert inflater != null; - inflater.inflate(R.layout.group_header, this); - - if (App.getDB().isReadOnly()) { - View readOnlyIndicator = findViewById(R.id.read_only); - readOnlyIndicator.setVisibility(VISIBLE); - } - - } - -} diff --git a/app/src/main/res/layout/group_header.xml b/app/src/main/res/layout/group_header.xml deleted file mode 100644 index d0b72b741..000000000 --- a/app/src/main/res/layout/group_header.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/list_nodes_with_add_button.xml b/app/src/main/res/layout/list_nodes_with_add_button.xml index a942b6643..f3020ef6b 100644 --- a/app/src/main/res/layout/list_nodes_with_add_button.xml +++ b/app/src/main/res/layout/list_nodes_with_add_button.xml @@ -56,24 +56,77 @@ app:popupTheme="?attr/toolbarPopupAppearance" android:elevation="4dp" tools:targetApi="lollipop"> - + android:layout_below="@+id/toolbar" + android:orientation="vertical"> + + + + + + + app:layout_behavior="@string/appbar_scrolling_view_behavior" + android:layout_below="@+id/toolbar" + android:background="?android:attr/windowBackground"> + + + + + + Date: Tue, 24 Jul 2018 18:02:08 +0200 Subject: [PATCH 2/7] Search in fragment --- .../keepass/activities/GroupActivity.java | 155 ++++++++---------- .../keepass/activities/ListNodesFragment.java | 39 ++++- .../keepass/adapters/NodeAdapter.java | 65 ++++++-- .../keepass/view/AddNodeButtonView.java | 2 +- .../main/res/layout/list_nodes_fragment.xml | 32 +++- .../res/layout/list_nodes_with_add_button.xml | 32 +--- 6 files changed, 177 insertions(+), 148 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 2e154469d..00891375d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -112,18 +112,11 @@ public class GroupActivity extends LockingActivity private ExpandableLayout toolbarPasteExpandableLayout; private Toolbar toolbarPaste; private ImageView iconView; - private View readOnlyView; private AddNodeButtonView addNodeButtonView; private TextView groupNameView; - private View notFoundView; - private View listContainer; private Database database; - private boolean addGroupEnabled = false; - private boolean addEntryEnabled = false; - private boolean isRoot = false; - private ListNodesFragment listNodesFragment; private boolean currentGroupIsASearch; @@ -219,54 +212,18 @@ public class GroupActivity extends LockingActivity // Initialize views iconView = findViewById(R.id.icon); - readOnlyView = findViewById(R.id.read_only); addNodeButtonView = findViewById(R.id.add_node_button); toolbar = findViewById(R.id.toolbar); groupNameView = findViewById(R.id.group_name); toolbarPasteExpandableLayout = findViewById(R.id.expandable_toolbar_paste_layout); toolbarPaste = findViewById(R.id.toolbar_paste); - notFoundView = findViewById(R.id.not_found_container); - listContainer = findViewById(R.id.nodes_list_fragment_container); invalidateOptionsMenu(); - rootGroup = database.getPwDatabase().getRootGroup(); - mCurrentGroup = retrieveCurrentGroup(getIntent(), savedInstanceState); - currentGroupIsASearch = Intent.ACTION_SEARCH.equals(getIntent().getAction()); - - Log.i(TAG, "Started creating tree"); - if ( mCurrentGroup == null ) { - Log.w(TAG, "Group was null"); - return; - } - - addNodeButtonView.enableAddGroup(addGroupEnabled); - addNodeButtonView.enableAddEntry(addEntryEnabled); - - toolbar.setTitle(""); - setSupportActionBar(toolbar); - - 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; - }); - - // Initialize thr fragment with list - listNodesFragment = (ListNodesFragment) getSupportFragmentManager() - .findFragmentByTag(LIST_NODES_FRAGMENT_TAG); - if (listNodesFragment == null) - listNodesFragment = ListNodesFragment.newInstance(mCurrentGroup, readOnly); - - // Attach fragment to content view - getSupportFragmentManager().beginTransaction().replace( - R.id.nodes_list_fragment_container, - listNodesFragment, - LIST_NODES_FRAGMENT_TAG) - .commit(); + // Get arg from intent or instance state + readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrIntent(savedInstanceState, getIntent()); + // Retrieve elements after an orientation change if (savedInstanceState != null) { if (savedInstanceState.containsKey(OLD_GROUP_TO_UPDATE_KEY)) oldGroupToUpdate = savedInstanceState.getParcelable(OLD_GROUP_TO_UPDATE_KEY); @@ -281,23 +238,55 @@ public class GroupActivity extends LockingActivity } } + rootGroup = database.getPwDatabase().getRootGroup(); + mCurrentGroup = retrieveCurrentGroup(getIntent(), savedInstanceState); + currentGroupIsASearch = Intent.ACTION_SEARCH.equals(getIntent().getAction()); + + Log.i(TAG, "Started creating tree"); + if ( mCurrentGroup == null ) { + Log.w(TAG, "Group was null"); + return; + } + + toolbar.setTitle(""); + setSupportActionBar(toolbar); + + 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; + }); + + // Initialize the fragment with the list + listNodesFragment = (ListNodesFragment) getSupportFragmentManager() + .findFragmentByTag(LIST_NODES_FRAGMENT_TAG); + if (listNodesFragment == null) + listNodesFragment = ListNodesFragment.newInstance(mCurrentGroup, readOnly, currentGroupIsASearch); + + // Attach fragment to content view + getSupportFragmentManager().beginTransaction().replace( + R.id.nodes_list_fragment_container, + listNodesFragment, + LIST_NODES_FRAGMENT_TAG) + .commit(); + + // Add listeners to the add buttons addNodeButtonView.setAddGroupClickListener(v -> GroupEditDialogFragment.build() .show(getSupportFragmentManager(), GroupEditDialogFragment.TAG_CREATE_GROUP)); addNodeButtonView.setAddEntryClickListener(v -> EntryEditActivity.launch(GroupActivity.this, mCurrentGroup)); - Log.i(TAG, "Finished creating tree"); - - if (isRoot) { - showWarnings(); - } - + // To init autofill entrySelectionMode = EntrySelectionHelper.isIntentInEntrySelectionMode(getIntent()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { autofillHelper = new AutofillHelper(); autofillHelper.retrieveAssistStructure(getIntent()); } + + Log.i(TAG, "Finished creating tree"); } @Override @@ -305,13 +294,15 @@ public class GroupActivity extends LockingActivity setIntent(intent); if (Intent.ACTION_SEARCH.equals(intent.getAction())) { // only one instance of search in backstack - openANewGroup(retrieveCurrentGroup(intent, null), !currentGroupIsASearch); + openANewGroup(retrieveCurrentGroup(intent, null), !currentGroupIsASearch, true); + currentGroupIsASearch = true; + } else { + currentGroupIsASearch = false; } - currentGroupIsASearch = Intent.ACTION_SEARCH.equals(getIntent().getAction()); } - private void openANewGroup(PwGroup group, boolean addToBackStack) { - ListNodesFragment newListNodeFragment = ListNodesFragment.newInstance(group, readOnly); + private void openANewGroup(PwGroup group, boolean addToBackStack, boolean isASearch) { + ListNodesFragment newListNodeFragment = ListNodesFragment.newInstance(group, readOnly, isASearch); FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); fragmentTransaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right) @@ -334,6 +325,7 @@ public class GroupActivity extends LockingActivity outState.putParcelable(NODE_TO_COPY_KEY, nodeToCopy); if (nodeToMove != null) outState.putParcelable(NODE_TO_MOVE_KEY, nodeToMove); + ReadOnlyHelper.onSaveInstanceState(outState, readOnly); super.onSaveInstanceState(outState); } @@ -364,14 +356,6 @@ public class GroupActivity extends LockingActivity currentGroup = database.getPwDatabase().getGroupByGroupId(pwGroupId); } - if (currentGroup != null) { - addGroupEnabled = !readOnly; - addEntryEnabled = !readOnly; - isRoot = (currentGroup == rootGroup); - if (!currentGroup.allowAddEntryIfIsRoot()) - addEntryEnabled = !isRoot && addEntryEnabled; - } - return currentGroup; } } @@ -393,13 +377,6 @@ public class GroupActivity extends LockingActivity } } - // Assign read only text - if (readOnly) { - readOnlyView.setVisibility(View.VISIBLE); - } else { - readOnlyView.setVisibility(View.GONE); - } - // Assign icon if (currentGroupIsASearch) { if (toolbar != null) { @@ -430,22 +407,24 @@ public class GroupActivity extends LockingActivity } } - // To show the " no search entry found " - if (currentGroupIsASearch && - (mCurrentGroup == null || mCurrentGroup.numbersOfChildEntries() < 1 )) { - listContainer.setVisibility(View.GONE); - notFoundView.setVisibility(View.VISIBLE); - } else { - listContainer.setVisibility(View.VISIBLE); - notFoundView.setVisibility(View.GONE); - } - // Show button if allowed - if (currentGroupIsASearch) - addNodeButtonView.setVisibility(View.GONE); - else { - addNodeButtonView.setVisibility(View.VISIBLE); - if (addNodeButtonView != null) + if (addNodeButtonView != null) { + + // To enable add button + boolean addGroupEnabled = !readOnly && !currentGroupIsASearch; + boolean addEntryEnabled = !readOnly && !currentGroupIsASearch; + if (mCurrentGroup != null) { + boolean isRoot = (mCurrentGroup == rootGroup); + if (!mCurrentGroup.allowAddEntryIfIsRoot()) + addEntryEnabled = !isRoot && addEntryEnabled; + if (isRoot) { + showWarnings(); + } + } + addNodeButtonView.enableAddGroup(addGroupEnabled); + addNodeButtonView.enableAddEntry(addEntryEnabled); + + if (addNodeButtonView.isEnable()) addNodeButtonView.showButton(); } } @@ -687,7 +666,7 @@ public class GroupActivity extends LockingActivity if (listNodesFragment != null && listNodesFragment.isEmpty()) { if (!PreferencesUtil.isEducationNewNodePerformed(this) - && addNodeButtonView.isVisible()) { + && addNodeButtonView.isEnable()) { TapTargetView.showFor(this, TapTarget.forView(findViewById(R.id.add_button), @@ -1089,7 +1068,7 @@ public class GroupActivity extends LockingActivity if (checkTimeIsAllowedOrFinish(this)) { startRecordTime(this); // Open a new group and add the current one in the backstack - openANewGroup(group, true); + openANewGroup(group, true, false); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java index 8c0517971..d4f654e2e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java @@ -34,25 +34,30 @@ public class ListNodesFragment extends StylishFragment implements private static final String TAG = ListNodesFragment.class.getName(); private static final String GROUP_KEY = "GROUP_KEY"; + private static final String IS_SEARCH = "IS_SEARCH"; private NodeAdapter.NodeClickCallback nodeClickCallback; private NodeAdapter.NodeMenuListener nodeMenuListener; private OnScrollListener onScrollListener; private RecyclerView listView; - protected PwGroup mCurrentGroup; - protected NodeAdapter mAdapter; + private PwGroup currentGroup; + private NodeAdapter mAdapter; + + private View notFoundView; + private boolean isASearchResult; // Preferences for sorting private SharedPreferences prefs; private boolean readOnly; - public static ListNodesFragment newInstance(PwGroup group, boolean readOnly) { + public static ListNodesFragment newInstance(PwGroup group, boolean readOnly, boolean isASearch) { Bundle bundle = new Bundle(); if (group != null) { bundle.putParcelable(GROUP_KEY, group); } + bundle.putBoolean(IS_SEARCH, isASearch); ReadOnlyHelper.putReadOnlyInBundle(bundle, readOnly); ListNodesFragment listNodesFragment = new ListNodesFragment(); listNodesFragment.setArguments(bundle); @@ -99,11 +104,17 @@ public class ListNodesFragment extends StylishFragment implements if (getArguments() != null) { // Contains all the group in element if (getArguments().containsKey(GROUP_KEY)) { - mCurrentGroup = getArguments().getParcelable(GROUP_KEY); + currentGroup = getArguments().getParcelable(GROUP_KEY); + } + + if (getArguments().containsKey(IS_SEARCH)) { + isASearchResult = getArguments().getBoolean(IS_SEARCH); } } - mAdapter = new NodeAdapter(getContextThemed(), getActivity().getMenuInflater(), readOnly); + mAdapter = new NodeAdapter(getContextThemed(), getActivity().getMenuInflater()); + mAdapter.setReadOnly(readOnly); + mAdapter.setIsASearchResult(isASearchResult); mAdapter.setOnNodeClickListener(nodeClickCallback); if (nodeMenuListener != null) { @@ -129,6 +140,7 @@ public class ListNodesFragment extends StylishFragment implements View rootView = inflater.cloneInContext(getContextThemed()) .inflate(R.layout.list_nodes_fragment, container, false); listView = rootView.findViewById(R.id.nodes_list); + notFoundView = rootView.findViewById(R.id.not_found_container); if (onScrollListener != null) { listView.addOnScrollListener(new RecyclerView.OnScrollListener() { @@ -148,11 +160,20 @@ public class ListNodesFragment extends StylishFragment implements super.onResume(); rebuildList(); + + if (isASearchResult && mAdapter.isEmpty()) { + // To show the " no search entry found " + listView.setVisibility(View.GONE); + notFoundView.setVisibility(View.VISIBLE); + } else { + listView.setVisibility(View.VISIBLE); + notFoundView.setVisibility(View.GONE); + } } public void rebuildList() { // Add elements to the list - mAdapter.rebuildList(mCurrentGroup); + mAdapter.rebuildList(currentGroup); assignListToNodeAdapter(listView); } @@ -174,7 +195,7 @@ public class ListNodesFragment extends StylishFragment implements // Tell the adapter to refresh it's list mAdapter.notifyChangeSort(sortNodeEnum, ascending, groupsBefore); - mAdapter.rebuildList(mCurrentGroup); + mAdapter.rebuildList(currentGroup); } @Override @@ -232,7 +253,7 @@ public class ListNodesFragment extends StylishFragment implements mAdapter.addNode(newNode); if (resultCode == EntryEditActivity.UPDATE_ENTRY_RESULT_CODE) { //mAdapter.updateLastNodeRegister(newNode); - mAdapter.rebuildList(mCurrentGroup); + mAdapter.rebuildList(currentGroup); } } else { Log.e(this.getClass().getName(), "New node can be retrieve in Activity Result"); @@ -259,7 +280,7 @@ public class ListNodesFragment extends StylishFragment implements } public PwGroup getMainGroup() { - return mCurrentGroup; + return currentGroup; } public interface OnScrollListener { diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java index 118d673f3..567c4efd9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java @@ -62,6 +62,7 @@ public class NodeAdapter extends RecyclerView.Adapter { private NodeMenuListener nodeMenuListener; private boolean activateContextMenu; private boolean readOnly; + private boolean isASearchResult; private int iconGroupColor; private int iconEntryColor; @@ -70,13 +71,14 @@ public class NodeAdapter extends RecyclerView.Adapter { * Create node list adapter with contextMenu or not * @param context Context to use */ - public NodeAdapter(final Context context, MenuInflater menuInflater, boolean readOnly) { + public NodeAdapter(final Context context, MenuInflater menuInflater) { this.inflater = LayoutInflater.from(context); this.menuInflater = menuInflater; this.context = context; assignPreferences(); this.activateContextMenu = false; - this.readOnly = readOnly; + this.readOnly = false; + this.isASearchResult = false; this.nodeSortedList = new SortedList<>(PwNode.class, new SortedListAdapterCallback(this) { @Override public int compare(PwNode item1, PwNode item2) { @@ -103,6 +105,14 @@ public class NodeAdapter extends RecyclerView.Adapter { taTextColor.recycle(); } + public void setReadOnly(boolean readOnly) { + this.readOnly = readOnly; + } + + public void setIsASearchResult(boolean isASearchResult) { + this.isASearchResult = isASearchResult; + } + public void setActivateContextMenu(boolean activate) { this.activateContextMenu = activate; } @@ -136,6 +146,14 @@ public class NodeAdapter extends RecyclerView.Adapter { } } + /** + * Determine if the adapter contains or not any element + * @return true if the list is empty + */ + public boolean isEmpty() { + return nodeSortedList.size() <= 0; + } + /** * Add a node to the list * @param node Node to add @@ -301,29 +319,44 @@ public class NodeAdapter extends RecyclerView.Adapter { public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) { menuInflater.inflate(R.menu.node_menu, contextMenu); + // Opening MenuItem menuItem = contextMenu.findItem(R.id.menu_open); menuItem.setOnMenuItemClickListener(mOnMyActionClickListener); + + // Edition if (readOnly || node.equals(App.getDB().getPwDatabase().getRecycleBin())) { contextMenu.removeItem(R.id.menu_edit); - contextMenu.removeItem(R.id.menu_copy); - contextMenu.removeItem(R.id.menu_move); - contextMenu.removeItem(R.id.menu_delete); } else { - // Edition menuItem = contextMenu.findItem(R.id.menu_edit); menuItem.setOnMenuItemClickListener(mOnMyActionClickListener); - // Copy (not for group) - if (node.getType().equals(PwNode.Type.ENTRY)) { - menuItem = contextMenu.findItem(R.id.menu_copy); - menuItem.setOnMenuItemClickListener(mOnMyActionClickListener); - } else { - // TODO COPY For Group - contextMenu.removeItem(R.id.menu_copy); - } - // Move + } + + // Copy (not for group) + if (readOnly + || isASearchResult + || node.equals(App.getDB().getPwDatabase().getRecycleBin()) + || node.getType().equals(PwNode.Type.GROUP)) { + // TODO COPY For Group + contextMenu.removeItem(R.id.menu_copy); + } else { + menuItem = contextMenu.findItem(R.id.menu_copy); + menuItem.setOnMenuItemClickListener(mOnMyActionClickListener); + } + + // Move + if (readOnly + || isASearchResult + || node.equals(App.getDB().getPwDatabase().getRecycleBin())) { + contextMenu.removeItem(R.id.menu_move); + } else { menuItem = contextMenu.findItem(R.id.menu_move); menuItem.setOnMenuItemClickListener(mOnMyActionClickListener); - // Deletion + } + + // Deletion + if (readOnly || node.equals(App.getDB().getPwDatabase().getRecycleBin())) { + contextMenu.removeItem(R.id.menu_delete); + } else { menuItem = contextMenu.findItem(R.id.menu_delete); menuItem.setOnMenuItemClickListener(mOnMyActionClickListener); } diff --git a/app/src/main/java/com/kunzisoft/keepass/view/AddNodeButtonView.java b/app/src/main/java/com/kunzisoft/keepass/view/AddNodeButtonView.java index b138b8da7..7386e83a3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/AddNodeButtonView.java +++ b/app/src/main/java/com/kunzisoft/keepass/view/AddNodeButtonView.java @@ -189,7 +189,7 @@ public class AddNodeButtonView extends RelativeLayout { } } - public boolean isVisible() { + public boolean isEnable() { return getVisibility() == VISIBLE; } diff --git a/app/src/main/res/layout/list_nodes_fragment.xml b/app/src/main/res/layout/list_nodes_fragment.xml index 2bdcfa380..6a1e5cd06 100644 --- a/app/src/main/res/layout/list_nodes_fragment.xml +++ b/app/src/main/res/layout/list_nodes_fragment.xml @@ -1,6 +1,30 @@ - \ No newline at end of file + android:layout_height="match_parent"> + + + + + + diff --git a/app/src/main/res/layout/list_nodes_with_add_button.xml b/app/src/main/res/layout/list_nodes_with_add_button.xml index f3020ef6b..cf83ec1f7 100644 --- a/app/src/main/res/layout/list_nodes_with_add_button.xml +++ b/app/src/main/res/layout/list_nodes_with_add_button.xml @@ -64,12 +64,6 @@ android:layout_alignParentStart="true" android:layout_below="@+id/toolbar" android:orientation="vertical"> - - - - - - - + android:background="?android:attr/windowBackground" /> Date: Tue, 24 Jul 2018 18:32:19 +0200 Subject: [PATCH 3/7] Fix timeout and new animation --- .../keepass/activities/GroupActivity.java | 64 +++++++++++-------- app/src/main/res/anim/slide_in_bottom.xml | 24 +++++++ app/src/main/res/anim/slide_in_top.xml | 24 +++++++ app/src/main/res/anim/slide_out_bottom.xml | 24 +++++++ app/src/main/res/anim/slide_out_top.xml | 24 +++++++ 5 files changed, 133 insertions(+), 27 deletions(-) create mode 100644 app/src/main/res/anim/slide_in_bottom.xml create mode 100644 app/src/main/res/anim/slide_in_top.xml create mode 100644 app/src/main/res/anim/slide_out_bottom.xml create mode 100644 app/src/main/res/anim/slide_out_top.xml diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 00891375d..83fb0261a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -294,27 +294,46 @@ public class GroupActivity extends LockingActivity setIntent(intent); if (Intent.ACTION_SEARCH.equals(intent.getAction())) { // only one instance of search in backstack - openANewGroup(retrieveCurrentGroup(intent, null), !currentGroupIsASearch, true); + openSearchGroup(retrieveCurrentGroup(intent, null)); currentGroupIsASearch = true; } else { currentGroupIsASearch = false; } } - private void openANewGroup(PwGroup group, boolean addToBackStack, boolean isASearch) { - ListNodesFragment newListNodeFragment = ListNodesFragment.newInstance(group, readOnly, isASearch); - FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); - fragmentTransaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, - R.anim.slide_in_left, R.anim.slide_out_right) - .replace(R.id.nodes_list_fragment_container, - newListNodeFragment, - LIST_NODES_FRAGMENT_TAG); - if (addToBackStack) - fragmentTransaction.addToBackStack(LIST_NODES_FRAGMENT_TAG); - fragmentTransaction.commit(); - listNodesFragment = newListNodeFragment; - mCurrentGroup = group; - assignGroupViewElements(); + private void openSearchGroup(PwGroup group) { + // if last group was a search, don't add to backstack + openGroup(group, !currentGroupIsASearch, true); + } + + private void openChildGroup(PwGroup group) { + openGroup(group, true, false); + } + + private void openGroup(PwGroup group, boolean addToBackStack, boolean isASearch) { + // Check Timeout + if (checkTimeIsAllowedOrFinish(this)) { + startRecordTime(this); + // Open a group in a new fragment + ListNodesFragment newListNodeFragment = ListNodesFragment.newInstance(group, readOnly, isASearch); + FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); + // Different animation + if (isASearch) + fragmentTransaction.setCustomAnimations(R.anim.slide_in_top, R.anim.slide_out_bottom, + R.anim.slide_in_bottom, R.anim.slide_out_top); + else + fragmentTransaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, + R.anim.slide_in_left, R.anim.slide_out_right); + fragmentTransaction.replace(R.id.nodes_list_fragment_container, + newListNodeFragment, + LIST_NODES_FRAGMENT_TAG); + if (addToBackStack) + fragmentTransaction.addToBackStack(LIST_NODES_FRAGMENT_TAG); + fragmentTransaction.commit(); + listNodesFragment = newListNodeFragment; + mCurrentGroup = group; + assignGroupViewElements(); + } } @Override @@ -445,7 +464,7 @@ public class GroupActivity extends LockingActivity if (assistStructure != null) { switch (node.getType()) { case GROUP: - openGroup((PwGroup) node); + openChildGroup((PwGroup) node); break; case ENTRY: // Build response with the entry selected @@ -459,7 +478,7 @@ public class GroupActivity extends LockingActivity if (entrySelectionMode) { switch (node.getType()) { case GROUP: - openGroup((PwGroup) node); + openChildGroup((PwGroup) node); break; case ENTRY: EntrySelectionHelper.buildResponseWhenEntrySelected(this, (PwEntry) node); @@ -469,7 +488,7 @@ public class GroupActivity extends LockingActivity } else { switch (node.getType()) { case GROUP: - openGroup((PwGroup) node); + openChildGroup((PwGroup) node); break; case ENTRY: EntryActivity.launch(this, (PwEntry) node, readOnly); @@ -1063,15 +1082,6 @@ public class GroupActivity extends LockingActivity } } - private void openGroup(PwGroup group) { - // Check Timeout - if (checkTimeIsAllowedOrFinish(this)) { - startRecordTime(this); - // Open a new group and add the current one in the backstack - openANewGroup(group, true, false); - } - } - @Override public void onAssignKeyDialogPositiveClick( boolean masterPasswordChecked, String masterPassword, diff --git a/app/src/main/res/anim/slide_in_bottom.xml b/app/src/main/res/anim/slide_in_bottom.xml new file mode 100644 index 000000000..775f82127 --- /dev/null +++ b/app/src/main/res/anim/slide_in_bottom.xml @@ -0,0 +1,24 @@ + + + + + + diff --git a/app/src/main/res/anim/slide_in_top.xml b/app/src/main/res/anim/slide_in_top.xml new file mode 100644 index 000000000..aef2d0412 --- /dev/null +++ b/app/src/main/res/anim/slide_in_top.xml @@ -0,0 +1,24 @@ + + + + + + diff --git a/app/src/main/res/anim/slide_out_bottom.xml b/app/src/main/res/anim/slide_out_bottom.xml new file mode 100644 index 000000000..617c3ecbd --- /dev/null +++ b/app/src/main/res/anim/slide_out_bottom.xml @@ -0,0 +1,24 @@ + + + + + + diff --git a/app/src/main/res/anim/slide_out_top.xml b/app/src/main/res/anim/slide_out_top.xml new file mode 100644 index 000000000..1dcc57787 --- /dev/null +++ b/app/src/main/res/anim/slide_out_top.xml @@ -0,0 +1,24 @@ + + + + + + From c76c3fd2be9daf24b5671f26804432a9b17c1997 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Wed, 25 Jul 2018 11:50:18 +0200 Subject: [PATCH 4/7] Fix fragment duplication --- .../keepass/activities/GroupActivity.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 83fb0261a..ade0b7341 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -37,6 +37,7 @@ import android.os.Handler; import android.preference.PreferenceManager; import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; +import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; import android.support.v7.widget.SearchView; import android.support.v7.widget.Toolbar; @@ -104,6 +105,7 @@ public class GroupActivity extends LockingActivity private static final String GROUP_ID_KEY = "GROUP_ID_KEY"; private static final String LIST_NODES_FRAGMENT_TAG = "LIST_NODES_FRAGMENT_TAG"; + private static final String SEARCH_FRAGMENT_TAG = "SEARCH_FRAGMENT_TAG"; 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"; @@ -324,9 +326,14 @@ public class GroupActivity extends LockingActivity else fragmentTransaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right); + + String tag = LIST_NODES_FRAGMENT_TAG; + if (isASearch) + tag = SEARCH_FRAGMENT_TAG; + fragmentTransaction.replace(R.id.nodes_list_fragment_container, newListNodeFragment, - LIST_NODES_FRAGMENT_TAG); + tag); if (addToBackStack) fragmentTransaction.addToBackStack(LIST_NODES_FRAGMENT_TAG); fragmentTransaction.commit(); @@ -1156,6 +1163,11 @@ public class GroupActivity extends LockingActivity mCurrentGroup = listNodesFragment.getMainGroup(); removeSearchInIntent(getIntent()); assignGroupViewElements(); + + // remove search fragment + Fragment searchFragment = getSupportFragmentManager().findFragmentByTag(SEARCH_FRAGMENT_TAG); + if (searchFragment != null) + getSupportFragmentManager().beginTransaction().remove(searchFragment).commit(); } } } From aa29aec40fa56ccbc2569e7c787f97e18af6aed9 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 26 Jul 2018 14:08:48 +0200 Subject: [PATCH 5/7] Add search suggestions --- .../keepass/tests/database/DeleteEntry.java | 6 +- .../keepass/tests/database/EntryV4.java | 6 +- .../keepass/activities/EntryActivity.java | 4 +- .../keepass/activities/EntryEditActivity.java | 4 +- .../keepass/activities/GroupActivity.java | 19 +++ .../adapters/SearchEntryCursorAdapter.java | 127 ++++++++++++++++++ .../kunzisoft/keepass/database/Database.java | 58 +++++++- .../kunzisoft/keepass/database/PwEntry.java | 2 +- .../kunzisoft/keepass/database/PwEntryV4.java | 24 +--- .../kunzisoft/keepass/database/PwGroupV4.java | 2 +- .../kunzisoft/keepass/database/PwIcon.java | 2 + .../keepass/database/PwIconCustom.java | 21 ++- .../keepass/database/PwIconFactory.java | 23 +--- .../keepass/database/PwIconStandard.java | 16 ++- .../kunzisoft/keepass/database/PwNode.java | 2 +- .../keepass/database/cursor/EntryCursor.java | 81 +++++++++++ .../iterator/EntrySearchStringIterator.java | 4 +- .../iterator/EntrySearchStringIteratorV3.java | 2 +- .../iterator/EntrySearchStringIteratorV4.java | 27 ++-- .../keepass/database/save/PwDbV4Output.java | 12 +- .../database/save/PwEntryOutputV3.java | 2 +- .../database/save/PwGroupOutputV3.java | 2 +- .../{ => search}/EntrySearchHandler.java | 4 +- .../{ => search}/EntrySearchHandlerAll.java | 5 +- .../{ => search}/EntrySearchHandlerV4.java | 5 +- .../database/{ => search}/EntrySearchV4.java | 5 +- .../{ => database}/search/SearchDbHelper.java | 14 +- .../{ => search}/SearchParameters.java | 2 +- .../{ => search}/SearchParametersV4.java | 2 +- .../keepass/icons/IconDrawableFactory.java | 10 +- .../kunzisoft/keepass/utils/SprEngineV4.java | 7 +- app/src/main/res/layout/list_nodes_entry.xml | 1 + app/src/main/res/layout/search_entry.xml | 51 +++++++ 33 files changed, 450 insertions(+), 102 deletions(-) create mode 100644 app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java rename app/src/main/java/com/kunzisoft/keepass/database/{ => search}/EntrySearchHandler.java (92%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => search}/EntrySearchHandlerAll.java (90%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => search}/EntrySearchHandlerV4.java (92%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => search}/EntrySearchV4.java (94%) rename app/src/main/java/com/kunzisoft/keepass/{ => database}/search/SearchDbHelper.java (93%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => search}/SearchParameters.java (97%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => search}/SearchParametersV4.java (96%) create mode 100644 app/src/main/res/layout/search_entry.xml diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java index 15f06fdb0..88597ae20 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java @@ -29,7 +29,7 @@ import com.kunzisoft.keepass.database.PwEntry; import com.kunzisoft.keepass.database.PwEntryV3; import com.kunzisoft.keepass.database.PwGroup; import com.kunzisoft.keepass.database.action.node.DeleteGroupRunnable; -import com.kunzisoft.keepass.search.SearchDbHelper; +import com.kunzisoft.keepass.database.search.SearchDbHelper; import java.util.List; @@ -72,8 +72,8 @@ public class DeleteEntry extends AndroidTestCase { // Verify the entries were removed from the search index SearchDbHelper dbHelp = new SearchDbHelper(ctx); - PwGroup results1 = dbHelp.search(db.getPwDatabase(), ENTRY1_NAME); - PwGroup results2 = dbHelp.search(db.getPwDatabase(), ENTRY2_NAME); + PwGroup results1 = dbHelp.search(db.getPwDatabase(), ENTRY1_NAME, 100); + PwGroup results2 = dbHelp.search(db.getPwDatabase(), ENTRY2_NAME, 100); assertEquals("Entry1 was not removed from the search results", 0, results1.numbersOfChildEntries()); assertEquals("Entry2 was not removed from the search results", 0, results2.numbersOfChildEntries()); diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java index c4a7f4cea..b20fa9b32 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java @@ -19,11 +19,11 @@ */ package com.kunzisoft.keepass.tests.database; -import junit.framework.TestCase; - import com.kunzisoft.keepass.database.PwDatabaseV4; import com.kunzisoft.keepass.database.PwEntryV4; +import junit.framework.TestCase; + public class EntryV4 extends TestCase { public void testBackup() { @@ -46,7 +46,7 @@ public class EntryV4 extends TestCase { entry.createBackup(db); PwEntryV4 backup = entry.getHistory().get(0); - entry.endToManageFieldReferences(); + entry.stopToManageFieldReferences(); assertEquals("Title2", backup.getTitle()); assertEquals("User2", backup.getUsername()); } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java index ebb6a7ff2..b992ab77a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java @@ -225,7 +225,7 @@ public class EntryActivity extends LockingHideActivity { startService(intent); } - mEntry.endToManageFieldReferences(); + mEntry.stopToManageFieldReferences(); } firstLaunchOfActivity = false; } @@ -369,7 +369,7 @@ public class EntryActivity extends LockingHideActivity { entryContentsView.assignExpiresDate(getString(R.string.never)); } - mEntry.endToManageFieldReferences(); + mEntry.stopToManageFieldReferences(); } @Override diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index 08a8512b8..d470c2685 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -429,7 +429,7 @@ public class EntryEditActivity extends LockingHideActivity } } - newEntry.endToManageFieldReferences(); + newEntry.stopToManageFieldReferences(); return newEntry; } @@ -484,7 +484,7 @@ public class EntryEditActivity extends LockingHideActivity } // Don't start the field reference manager, we want to see the raw ref - mEntry.endToManageFieldReferences(); + mEntry.stopToManageFieldReferences(); entryTitleView.setText(mEntry.getTitle()); entryUserNameView.setText(mEntry.getUsername()); diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index ade0b7341..2f54bb21b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -53,6 +53,7 @@ import com.getkeepsafe.taptargetview.TapTarget; import com.getkeepsafe.taptargetview.TapTargetView; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.adapters.NodeAdapter; +import com.kunzisoft.keepass.adapters.SearchEntryCursorAdapter; import com.kunzisoft.keepass.app.App; import com.kunzisoft.keepass.autofill.AutofillHelper; import com.kunzisoft.keepass.database.Database; @@ -131,6 +132,8 @@ public class GroupActivity extends LockingActivity private boolean entrySelectionMode; private AutofillHelper autofillHelper; + private SearchEntryCursorAdapter searchSuggestionAdapter; + // After a database creation public static void launch(Activity act) { launch(act, READ_ONLY_DEFAULT); @@ -288,6 +291,9 @@ public class GroupActivity extends LockingActivity autofillHelper.retrieveAssistStructure(getIntent()); } + // Search suggestion + searchSuggestionAdapter = new SearchEntryCursorAdapter(this, database); + Log.i(TAG, "Finished creating tree"); } @@ -845,6 +851,19 @@ public class GroupActivity extends LockingActivity if (searchView != null) { searchView.setSearchableInfo(searchManager.getSearchableInfo(new ComponentName(this, GroupActivity.class))); searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default + searchView.setSuggestionsAdapter(searchSuggestionAdapter); + searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() { + @Override + public boolean onSuggestionClick(int position) { + onNodeClick(searchSuggestionAdapter.getEntryFromPosition(position)); + return true; + } + + @Override + public boolean onSuggestionSelect(int position) { + return true; + } + }); } MenuUtil.contributionMenuInflater(inflater, menu); diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java new file mode 100644 index 000000000..85f5d09ce --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java @@ -0,0 +1,127 @@ +/* + * 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 . + * + */ +package com.kunzisoft.keepass.adapters; + +import android.content.Context; +import android.content.res.TypedArray; +import android.database.Cursor; +import android.graphics.Color; +import android.support.v4.widget.CursorAdapter; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import com.kunzisoft.keepass.R; +import com.kunzisoft.keepass.database.Database; +import com.kunzisoft.keepass.database.PwEntry; +import com.kunzisoft.keepass.database.PwIcon; +import com.kunzisoft.keepass.database.PwIconFactory; +import com.kunzisoft.keepass.database.PwIconStandard; +import com.kunzisoft.keepass.database.cursor.EntryCursor; +import com.kunzisoft.keepass.icons.IconPackChooser; + +import java.util.UUID; + +public class SearchEntryCursorAdapter extends CursorAdapter { + + private LayoutInflater cursorInflater; + private Database database; + private int iconColor; + + public SearchEntryCursorAdapter(Context context, Database database) { + super(context, null, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); + cursorInflater = (LayoutInflater) context.getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + this.database = database; + + // Get the icon color + int[] attrTextColor = {R.attr.textColorInverse}; + TypedArray taTextColor = context.getTheme().obtainStyledAttributes(attrTextColor); + this.iconColor = taTextColor.getColor(0, Color.WHITE); + taTextColor.recycle(); + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + + View view = cursorInflater.inflate(R.layout.search_entry, parent ,false); + ViewHolder viewHolder = new ViewHolder(); + viewHolder.imageViewIcon = view.findViewById(R.id.entry_icon); + viewHolder.textViewTitle = view.findViewById(R.id.entry_text); + view.setTag(viewHolder); + + return view; + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + + // Retrieve elements from cursor + PwIconFactory iconFactory = database.getPwDatabase().getIconFactory(); + PwIcon icon = iconFactory.getIcon( + new UUID(cursor.getLong(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_CUSTOM_UUID_MOST_SIGNIFICANT_BITS)), + cursor.getLong(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_CUSTOM_UUID_LEAST_SIGNIFICANT_BITS)))); + if (icon.isUnknown()) { + icon = iconFactory.getIcon(cursor.getInt(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_STANDARD))); + if (icon.isUnknown()) + icon = iconFactory.getKeyIcon(); + } + String title = cursor.getString( cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_TITLE) ); + + ViewHolder viewHolder = (ViewHolder) view.getTag(); + + // Assign image + if (IconPackChooser.getSelectedIconPack(context).tintable()) { + database.getDrawFactory().assignDatabaseIconTo(context, viewHolder.imageViewIcon, icon, true, iconColor); + } else { + database.getDrawFactory().assignDatabaseIconTo(context, viewHolder.imageViewIcon, icon); + } + // Assign title + viewHolder.textViewTitle.setText(title); + } + + private static class ViewHolder { + ImageView imageViewIcon; + TextView textViewTitle; + } + + @Override + public Cursor runQueryOnBackgroundThread(CharSequence constraint) { + return database.searchEntry(constraint.toString()); + } + + public PwEntry getEntryFromPosition(int position) { + PwEntry pwEntry = null; + + Cursor cursor = this.getCursor(); + if (cursor.moveToFirst() + && + cursor.move(position)) { + + pwEntry = database.createEntry(); + database.populateEntry(pwEntry, cursor); + + } + return pwEntry; + } + +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/Database.java b/app/src/main/java/com/kunzisoft/keepass/database/Database.java index 1f4830c52..90cd2f868 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/Database.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/Database.java @@ -21,19 +21,21 @@ package com.kunzisoft.keepass.database; import android.content.Context; import android.content.res.Resources; +import android.database.Cursor; import android.net.Uri; import android.util.Log; import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine; import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory; +import com.kunzisoft.keepass.database.cursor.EntryCursor; import com.kunzisoft.keepass.database.exception.ContentFileNotFoundException; import com.kunzisoft.keepass.database.exception.InvalidDBException; import com.kunzisoft.keepass.database.exception.PwDbOutputException; import com.kunzisoft.keepass.database.load.Importer; import com.kunzisoft.keepass.database.load.ImporterFactory; import com.kunzisoft.keepass.database.save.PwDbOutput; +import com.kunzisoft.keepass.database.search.SearchDbHelper; import com.kunzisoft.keepass.icons.IconDrawableFactory; -import com.kunzisoft.keepass.search.SearchDbHelper; import com.kunzisoft.keepass.tasks.ProgressTaskUpdater; import com.kunzisoft.keepass.utils.UriUtil; @@ -179,13 +181,17 @@ public class Database { } public PwGroup search(String str) { + return search(str, Integer.MAX_VALUE); + } + + public PwGroup search(String str, int max) { if (searchHelper == null) { return null; } try { switch (pm.getVersion()) { case V3: - return ((SearchDbHelper.SearchDbHelperV3) searchHelper).search(((PwDatabaseV3) pm), str); + return ((SearchDbHelper.SearchDbHelperV3) searchHelper).search(((PwDatabaseV3) pm), str, max); case V4: - return ((SearchDbHelper.SearchDbHelperV4) searchHelper).search(((PwDatabaseV4) pm), str); + return ((SearchDbHelper.SearchDbHelperV4) searchHelper).search(((PwDatabaseV4) pm), str, max); } } catch (Exception e) { Log.e(TAG, "Search can't be performed with this SearchHelper", e); @@ -193,6 +199,46 @@ public class Database { return null; } + public Cursor searchEntry(String query) { + final EntryCursor cursor = new EntryCursor(); + + // TODO real content provider + if (!query.isEmpty()) { + PwGroup searchResult = search(query, 6); + for (int i=0; i < searchResult.numbersOfChildEntries(); i++) { + PwEntry entry = searchResult.getChildEntryAt(i); + try { + switch (getPwDatabase().getVersion()) { + case V3: + EntryCursor.addEntry(cursor, (PwEntryV3) entry, i); + case V4: + EntryCursor.addEntry(cursor, (PwEntryV4) entry, i); + } + } catch (Exception e) { + Log.e(TAG, "This version of PwGroup can't be populated", e); + } + } + } + return cursor; + } + + public void populateEntry(PwEntry pwEntry, Cursor cursor) { + // TODO invert field reference manager + pwEntry.startToManageFieldReferences(getPwDatabase()); + PwIconFactory iconFactory = getPwDatabase().getIconFactory(); + try { + switch (getPwDatabase().getVersion()) { + case V3: + EntryCursor.populateEntry(cursor, (PwEntryV3) pwEntry, iconFactory); + case V4: + EntryCursor.populateEntry(cursor, (PwEntryV4) pwEntry, iconFactory); + } + } catch (Exception e) { + Log.e(TAG, "This version of PwGroup can't be populated", e); + } + pwEntry.stopToManageFieldReferences(); + } + public void saveData(Context ctx) throws IOException, PwDbOutputException { saveData(ctx, mUri); } @@ -464,7 +510,11 @@ public class Database { } } - public PwEntry createEntry(PwGroup parent) { + public PwEntry createEntry() { + return createEntry(null); + } + + public PwEntry createEntry(@Nullable PwGroup parent) { PwEntry newPwEntry = null; try { switch (getPwDatabase().getVersion()) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwEntry.java b/app/src/main/java/com/kunzisoft/keepass/database/PwEntry.java index fd25c2ec6..9df7cd154 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwEntry.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/PwEntry.java @@ -76,7 +76,7 @@ public abstract class PwEntry extends PwNode { } public void startToManageFieldReferences(PwDatabase db) {} - public void endToManageFieldReferences() {} + public void stopToManageFieldReferences() {} public abstract String getTitle(); public abstract void setTitle(String title); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwEntryV4.java b/app/src/main/java/com/kunzisoft/keepass/database/PwEntryV4.java index 3b9262c3e..6170f3c5d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwEntryV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/PwEntryV4.java @@ -169,7 +169,7 @@ public class PwEntryV4 extends PwEntry implements ITimeLogger { } @Override - public void endToManageFieldReferences() { + public void stopToManageFieldReferences() { this.mDatabase = null; this.mDecodeRef = false; } @@ -205,41 +205,31 @@ public class PwEntryV4 extends PwEntry implements ITimeLogger { @Override public void setTitle(String title) { - PwDatabaseV4 db = mDatabase; - boolean protect = db.getMemoryProtection().protectTitle; - + boolean protect = (mDatabase != null) && mDatabase.getMemoryProtection().protectTitle; setProtectedString(STR_TITLE, title, protect); } @Override public void setUsername(String user) { - PwDatabaseV4 db = mDatabase; - boolean protect = db.getMemoryProtection().protectUserName; - + boolean protect = (mDatabase != null) && mDatabase.getMemoryProtection().protectUserName; setProtectedString(STR_USERNAME, user, protect); } @Override public void setPassword(String pass) { - PwDatabaseV4 db = mDatabase; - boolean protect = db.getMemoryProtection().protectPassword; - + boolean protect = (mDatabase != null) && mDatabase.getMemoryProtection().protectPassword; setProtectedString(STR_PASSWORD, pass, protect); } @Override public void setUrl(String url) { - PwDatabaseV4 db = mDatabase; - boolean protect = db.getMemoryProtection().protectUrl; - + boolean protect = (mDatabase != null) && mDatabase.getMemoryProtection().protectUrl; setProtectedString(STR_URL, url, protect); } @Override public void setNotes(String notes) { - PwDatabaseV4 db = mDatabase; - boolean protect = db.getMemoryProtection().protectNotes; - + boolean protect = (mDatabase != null) && mDatabase.getMemoryProtection().protectNotes; setProtectedString(STR_NOTES, notes, protect); } @@ -287,7 +277,7 @@ public class PwEntryV4 extends PwEntry implements ITimeLogger { @Override public PwIcon getIcon() { - if (customIcon == null || customIcon.uuid.equals(PwDatabase.UUID_ZERO)) { + if (customIcon == null || customIcon.getUUID().equals(PwDatabase.UUID_ZERO)) { return super.getIcon(); } else { return customIcon; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwGroupV4.java b/app/src/main/java/com/kunzisoft/keepass/database/PwGroupV4.java index c65d88e8d..959a741af 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwGroupV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/PwGroupV4.java @@ -225,7 +225,7 @@ public class PwGroupV4 extends PwGroup implemen @Override public PwIcon getIcon() { - if (customIcon == null || customIcon.uuid.equals(PwDatabase.UUID_ZERO)) { + if (customIcon == null || customIcon.getUUID().equals(PwDatabase.UUID_ZERO)) { return super.getIcon(); } else { return customIcon; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwIcon.java b/app/src/main/java/com/kunzisoft/keepass/database/PwIcon.java index bd8f37a6b..db23e3425 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwIcon.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/PwIcon.java @@ -32,6 +32,8 @@ public abstract class PwIcon implements Parcelable { protected PwIcon(Parcel in) {} + public abstract boolean isUnknown(); + @Override public int describeContents() { return 0; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwIconCustom.java b/app/src/main/java/com/kunzisoft/keepass/database/PwIconCustom.java index 7299037d3..a1d9c132b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwIconCustom.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/PwIconCustom.java @@ -26,8 +26,8 @@ import java.util.UUID; public class PwIconCustom extends PwIcon { public static final PwIconCustom ZERO = new PwIconCustom(PwDatabase.UUID_ZERO, new byte[0]); - public final UUID uuid; - public byte[] imageData; + private final UUID uuid; + private byte[] imageData; public PwIconCustom(UUID uuid, byte[] data) { super(); @@ -48,6 +48,23 @@ public class PwIconCustom extends PwIcon { } @Override + public boolean isUnknown() { + return uuid == null || this.equals(ZERO); + } + + public UUID getUUID() { + return uuid; + } + + public byte[] getImageData() { + return imageData; + } + + public void setImageData(byte[] imageData) { + this.imageData = imageData; + } + + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeSerializable(uuid); dest.writeByteArray(imageData); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwIconFactory.java b/app/src/main/java/com/kunzisoft/keepass/database/PwIconFactory.java index 480ac531c..740341ea8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwIconFactory.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/PwIconFactory.java @@ -46,8 +46,8 @@ public class PwIconFactory { } public PwIconStandard getFolderIcon() { - return getIcon(PwIconStandard.FOLDER); - } + return getIcon(PwIconStandard.FOLDER); + } public PwIconStandard getIcon(int iconId) { PwIconStandard icon = (PwIconStandard) cache.get(iconId); @@ -71,25 +71,8 @@ public class PwIconFactory { return icon; } - public PwIconCustom getIcon(UUID iconUuid, byte[] data) { - PwIconCustom icon = (PwIconCustom) customCache.get(iconUuid); - - if (icon == null) { - icon = new PwIconCustom(iconUuid, data); - customCache.put(iconUuid, icon); - } else { - icon.imageData = data; - } - - return icon; - } - - public void setIconData(UUID iconUuid, byte[] data) { - getIcon(iconUuid, data); - } - public void put(PwIconCustom icon) { - customCache.put(icon.uuid, icon); + customCache.put(icon.getUUID(), icon); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwIconStandard.java b/app/src/main/java/com/kunzisoft/keepass/database/PwIconStandard.java index 961eff1bb..f0c73e0f9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwIconStandard.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/PwIconStandard.java @@ -22,12 +22,17 @@ package com.kunzisoft.keepass.database; import android.os.Parcel; public class PwIconStandard extends PwIcon { - public final int iconId; + private final int iconId; + public static final int UNKNOWN = -1; public static final int KEY = 0; public static final int TRASH = 43; public static final int FOLDER = 48; + public PwIconStandard() { + this.iconId = KEY; + } + public PwIconStandard(int iconId) { this.iconId = iconId; } @@ -42,6 +47,15 @@ public class PwIconStandard extends PwIcon { } @Override + public boolean isUnknown() { + return iconId == UNKNOWN; + } + + public int getIconId() { + return iconId; + } + + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(iconId); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwNode.java b/app/src/main/java/com/kunzisoft/keepass/database/PwNode.java index 5080afcc1..db426bad3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwNode.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/PwNode.java @@ -33,7 +33,7 @@ import org.joda.time.LocalDate; public abstract class PwNode implements ISmallTimeLogger, Parcelable, Cloneable { protected Parent parent = null; - protected PwIconStandard icon = new PwIconStandard(0); + protected PwIconStandard icon = new PwIconStandard(); protected PwDate creation = new PwDate(); protected PwDate lastMod = new PwDate(); protected PwDate lastAccess = new PwDate(); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java new file mode 100644 index 000000000..cddbb0b41 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java @@ -0,0 +1,81 @@ +package com.kunzisoft.keepass.database.cursor; + +import android.database.Cursor; +import android.database.MatrixCursor; +import android.provider.BaseColumns; +import android.util.Log; + +import com.kunzisoft.keepass.database.PwDatabase; +import com.kunzisoft.keepass.database.PwEntry; +import com.kunzisoft.keepass.database.PwEntryV3; +import com.kunzisoft.keepass.database.PwEntryV4; +import com.kunzisoft.keepass.database.PwIconCustom; +import com.kunzisoft.keepass.database.PwIconFactory; +import com.kunzisoft.keepass.database.PwIconStandard; + +import java.util.UUID; + +public class EntryCursor extends MatrixCursor { + + public static final String _ID = BaseColumns._ID; + public static final String COLUMN_INDEX_UUID_MOST_SIGNIFICANT_BITS = "UUIDMostSignificantBits"; + public static final String COLUMN_INDEX_UUID_LEAST_SIGNIFICANT_BITS = "UUIDLeastSignificantBits"; + public static final String COLUMN_INDEX_TITLE = "title"; + public static final String COLUMN_INDEX_ICON_STANDARD = "iconStandard"; + public static final String COLUMN_INDEX_ICON_CUSTOM_UUID_MOST_SIGNIFICANT_BITS = "iconCustomUUIDMostSignificantBits"; + public static final String COLUMN_INDEX_ICON_CUSTOM_UUID_LEAST_SIGNIFICANT_BITS = "iconCustomUUIDLeastSignificantBits"; + + public EntryCursor() { + super(new String[]{ _ID, + COLUMN_INDEX_UUID_MOST_SIGNIFICANT_BITS, + COLUMN_INDEX_UUID_LEAST_SIGNIFICANT_BITS, + COLUMN_INDEX_TITLE, + COLUMN_INDEX_ICON_STANDARD, + COLUMN_INDEX_ICON_CUSTOM_UUID_MOST_SIGNIFICANT_BITS, + COLUMN_INDEX_ICON_CUSTOM_UUID_LEAST_SIGNIFICANT_BITS}); + } + + public static void addEntry(MatrixCursor cursor, PwEntryV3 entry, int id) { + cursor.addRow(new Object[] {id, + entry.getUUID().getMostSignificantBits(), + entry.getUUID().getLeastSignificantBits(), + entry.getTitle(), + entry.getIconStandard().getIconId(), + PwDatabase.UUID_ZERO.getMostSignificantBits(), + PwDatabase.UUID_ZERO.getLeastSignificantBits()}); + } + + public static void addEntry(MatrixCursor cursor, PwEntryV4 entry, int id) { + cursor.addRow(new Object[] {id, + entry.getUUID().getMostSignificantBits(), + entry.getUUID().getLeastSignificantBits(), + entry.getTitle(), + entry.getIconStandard().getIconId(), + entry.getCustomIcon().getUUID().getMostSignificantBits(), + entry.getCustomIcon().getUUID().getLeastSignificantBits()}); + } + + private static void populateEntryBaseVersion(Cursor cursor, PwEntry pwEntry, PwIconFactory iconFactory) { + pwEntry.setUUID( + new UUID(cursor.getLong(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_UUID_MOST_SIGNIFICANT_BITS)), + cursor.getLong(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_UUID_LEAST_SIGNIFICANT_BITS)))); + pwEntry.setTitle(cursor.getString(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_TITLE))); + + PwIconStandard iconStandard = iconFactory.getIcon(cursor.getInt(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_STANDARD))); + pwEntry.setIcon(iconStandard); + } + + public static void populateEntry(Cursor cursor, PwEntryV3 pwEntry, PwIconFactory iconFactory) { + populateEntryBaseVersion(cursor, pwEntry, iconFactory); + } + + public static void populateEntry(Cursor cursor, PwEntryV4 pwEntry, PwIconFactory iconFactory) { + populateEntryBaseVersion(cursor, pwEntry, iconFactory); + + PwIconCustom iconCustom = iconFactory.getIcon( + new UUID(cursor.getLong(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_CUSTOM_UUID_MOST_SIGNIFICANT_BITS)), + cursor.getLong(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_CUSTOM_UUID_LEAST_SIGNIFICANT_BITS)))); + pwEntry.setCustomIcon(iconCustom); + } + +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.java b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.java index 36ae095a4..da3c675ff 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.java @@ -22,8 +22,8 @@ package com.kunzisoft.keepass.database.iterator; import com.kunzisoft.keepass.database.PwEntry; import com.kunzisoft.keepass.database.PwEntryV3; import com.kunzisoft.keepass.database.PwEntryV4; -import com.kunzisoft.keepass.database.SearchParameters; -import com.kunzisoft.keepass.database.SearchParametersV4; +import com.kunzisoft.keepass.database.search.SearchParameters; +import com.kunzisoft.keepass.database.search.SearchParametersV4; import java.util.Iterator; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java index 9a690ce20..b6d6dbc8d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java @@ -20,7 +20,7 @@ package com.kunzisoft.keepass.database.iterator; import com.kunzisoft.keepass.database.PwEntryV3; -import com.kunzisoft.keepass.database.SearchParameters; +import com.kunzisoft.keepass.database.search.SearchParameters; import java.util.NoSuchElementException; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.java b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.java index a73e69dc8..3e08817a4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.java @@ -20,7 +20,7 @@ package com.kunzisoft.keepass.database.iterator; import com.kunzisoft.keepass.database.PwEntryV4; -import com.kunzisoft.keepass.database.SearchParametersV4; +import com.kunzisoft.keepass.database.search.SearchParametersV4; import com.kunzisoft.keepass.database.security.ProtectedString; import java.util.Iterator; @@ -78,18 +78,19 @@ public class EntrySearchStringIteratorV4 extends EntrySearchStringIterator { } private boolean searchInField(String key) { - if (key.equals(PwEntryV4.STR_TITLE)) { - return sp.searchInTitles; - } else if (key.equals(PwEntryV4.STR_USERNAME)) { - return sp.searchInUserNames; - } else if (key.equals(PwEntryV4.STR_PASSWORD)) { - return sp.searchInPasswords; - } else if (key.equals(PwEntryV4.STR_URL)) { - return sp.searchInUrls; - } else if (key.equals(PwEntryV4.STR_NOTES)) { - return sp.searchInNotes; - } else { - return sp.searchInOther; + switch (key) { + case PwEntryV4.STR_TITLE: + return sp.searchInTitles; + case PwEntryV4.STR_USERNAME: + return sp.searchInUserNames; + case PwEntryV4.STR_PASSWORD: + return sp.searchInPasswords; + case PwEntryV4.STR_URL: + return sp.searchInUrls; + case PwEntryV4.STR_NOTES: + return sp.searchInNotes; + default: + return sp.searchInOther; } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java index 194dbf4cd..214af00b6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java @@ -350,10 +350,10 @@ public class PwDbV4Output extends PwDbOutput { writeObject(PwDatabaseV4XML.ElemUuid, group.getUUID()); writeObject(PwDatabaseV4XML.ElemName, group.getName()); writeObject(PwDatabaseV4XML.ElemNotes, group.getNotes()); - writeObject(PwDatabaseV4XML.ElemIcon, group.getIconStandard().iconId); + writeObject(PwDatabaseV4XML.ElemIcon, group.getIconStandard().getIconId()); if (!group.getCustomIcon().equals(PwIconCustom.ZERO)) { - writeObject(PwDatabaseV4XML.ElemCustomIconID, group.getCustomIcon().uuid); + writeObject(PwDatabaseV4XML.ElemCustomIconID, group.getCustomIcon().getUUID()); } writeList(PwDatabaseV4XML.ElemTimes, group); @@ -375,10 +375,10 @@ public class PwDbV4Output extends PwDbOutput { xml.startTag(null, PwDatabaseV4XML.ElemEntry); writeObject(PwDatabaseV4XML.ElemUuid, entry.getUUID()); - writeObject(PwDatabaseV4XML.ElemIcon, entry.getIconStandard().iconId); + writeObject(PwDatabaseV4XML.ElemIcon, entry.getIconStandard().getIconId()); if (!entry.getCustomIcon().equals(PwIconCustom.ZERO)) { - writeObject(PwDatabaseV4XML.ElemCustomIconID, entry.getCustomIcon().uuid); + writeObject(PwDatabaseV4XML.ElemCustomIconID, entry.getCustomIcon().getUUID()); } writeObject(PwDatabaseV4XML.ElemFgColor, entry.getForegroundColor()); @@ -701,8 +701,8 @@ public class PwDbV4Output extends PwDbOutput { for (PwIconCustom icon : customIcons) { xml.startTag(null, PwDatabaseV4XML.ElemCustomIconItem); - writeObject(PwDatabaseV4XML.ElemCustomIconItemID, icon.uuid); - writeObject(PwDatabaseV4XML.ElemCustomIconItemData, String.valueOf(Base64Coder.encode(icon.imageData))); + writeObject(PwDatabaseV4XML.ElemCustomIconItemID, icon.getUUID()); + writeObject(PwDatabaseV4XML.ElemCustomIconItemData, String.valueOf(Base64Coder.encode(icon.getImageData()))); xml.endTag(null, PwDatabaseV4XML.ElemCustomIconItem); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java index 2a640e843..c9ba3feda 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java @@ -84,7 +84,7 @@ public class PwEntryOutputV3 { // Image ID mOS.write(IMAGEID_FIELD_TYPE); mOS.write(LONG_FOUR); - mOS.write(LEDataOutputStream.writeIntBuf(mPE.getIconStandard().iconId)); + mOS.write(LEDataOutputStream.writeIntBuf(mPE.getIconStandard().getIconId())); // Title //byte[] title = mPE.title.getBytes("UTF-8"); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwGroupOutputV3.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwGroupOutputV3.java index 74dbff568..9af15826a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwGroupOutputV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwGroupOutputV3.java @@ -93,7 +93,7 @@ public class PwGroupOutputV3 { // Image ID mOS.write(IMAGEID_FIELD_TYPE); mOS.write(IMAGEID_FIELD_SIZE); - mOS.write(LEDataOutputStream.writeIntBuf(mPG.getIconStandard().iconId)); + mOS.write(LEDataOutputStream.writeIntBuf(mPG.getIconStandard().getIconId())); // Level mOS.write(LEVEL_FIELD_TYPE); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/EntrySearchHandler.java b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandler.java similarity index 92% rename from app/src/main/java/com/kunzisoft/keepass/database/EntrySearchHandler.java rename to app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandler.java index 24bed9df9..99753a8e2 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/EntrySearchHandler.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandler.java @@ -17,8 +17,10 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.search; +import com.kunzisoft.keepass.database.EntryHandler; +import com.kunzisoft.keepass.database.PwEntry; import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator; import java.util.Date; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/EntrySearchHandlerAll.java b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerAll.java similarity index 90% rename from app/src/main/java/com/kunzisoft/keepass/database/EntrySearchHandlerAll.java rename to app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerAll.java index 87e5f175d..256bd7d53 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/EntrySearchHandlerAll.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerAll.java @@ -17,7 +17,10 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.search; + +import com.kunzisoft.keepass.database.EntryHandler; +import com.kunzisoft.keepass.database.PwEntry; import java.util.Date; import java.util.List; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/EntrySearchHandlerV4.java b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java similarity index 92% rename from app/src/main/java/com/kunzisoft/keepass/database/EntrySearchHandlerV4.java rename to app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java index b6dd0a1ce..57deea143 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/EntrySearchHandlerV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java @@ -17,8 +17,11 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.search; +import com.kunzisoft.keepass.database.PwEntry; +import com.kunzisoft.keepass.database.PwEntryV4; +import com.kunzisoft.keepass.database.PwGroup; import com.kunzisoft.keepass.utils.StrUtil; import com.kunzisoft.keepass.utils.UuidUtil; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/EntrySearchV4.java b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchV4.java similarity index 94% rename from app/src/main/java/com/kunzisoft/keepass/database/EntrySearchV4.java rename to app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchV4.java index 50520f01f..1faa70801 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/EntrySearchV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchV4.java @@ -17,8 +17,11 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.search; +import com.kunzisoft.keepass.database.EntryHandler; +import com.kunzisoft.keepass.database.PwEntryV4; +import com.kunzisoft.keepass.database.PwGroupV4; import com.kunzisoft.keepass.utils.StrUtil; import java.util.ArrayList; diff --git a/app/src/main/java/com/kunzisoft/keepass/search/SearchDbHelper.java b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java similarity index 93% rename from app/src/main/java/com/kunzisoft/keepass/search/SearchDbHelper.java rename to app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java index 5cd4b7352..fd4a7a989 100644 --- a/app/src/main/java/com/kunzisoft/keepass/search/SearchDbHelper.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.search; +package com.kunzisoft.keepass.database.search; import android.content.Context; import android.content.SharedPreferences; @@ -54,10 +54,9 @@ public class SearchDbHelper worklist = new LinkedList<>(); if (pm.getRootGroup() != null) { worklist.add(pm.getRootGroup()); } - + while (worklist.size() != 0) { PwGroupSearch top = worklist.remove(); if (pm.isGroupSearchable(top, isOmitBackup)) { for (PwEntrySearch entry : top.getChildEntries()) { processEntries(entry, group.getChildEntries(), qStr, loc); + if (group.numbersOfChildEntries() >= max) + return group; } for (PwGroupSearch childGroup : top.getChildGroups()) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/SearchParameters.java b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchParameters.java similarity index 97% rename from app/src/main/java/com/kunzisoft/keepass/database/SearchParameters.java rename to app/src/main/java/com/kunzisoft/keepass/database/search/SearchParameters.java index af87198bf..6b671d754 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/SearchParameters.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchParameters.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.search; /** * @author bpellin diff --git a/app/src/main/java/com/kunzisoft/keepass/database/SearchParametersV4.java b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchParametersV4.java similarity index 96% rename from app/src/main/java/com/kunzisoft/keepass/database/SearchParametersV4.java rename to app/src/main/java/com/kunzisoft/keepass/database/search/SearchParametersV4.java index f02c251b7..b53324a0d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/SearchParametersV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchParametersV4.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.search; public class SearchParametersV4 extends SearchParameters implements Cloneable { public static SearchParametersV4 DEFAULT = new SearchParametersV4(); diff --git a/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.java b/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.java index bc192f9c8..aacccf67f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.java +++ b/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.java @@ -221,7 +221,7 @@ public class IconDrawableFactory { * @return The drawable */ private Drawable getIconDrawable(Context context, PwIconStandard icon, boolean isTint, int tintColor) { - int resId = IconPackChooser.getSelectedIconPack(context).iconToResId(icon.iconId); + int resId = IconPackChooser.getSelectedIconPack(context).iconToResId(icon.getIconId()); return getIconDrawable(context, resId, isTint, tintColor); } @@ -289,14 +289,14 @@ public class IconDrawableFactory { return blank; } - Drawable draw = (Drawable) customIconMap.get(icon.uuid); + Drawable draw = (Drawable) customIconMap.get(icon.getUUID()); if (draw == null) { - if (icon.imageData == null) { + if (icon.getImageData() == null) { return blank; } - Bitmap bitmap = BitmapFactory.decodeByteArray(icon.imageData, 0, icon.imageData.length); + Bitmap bitmap = BitmapFactory.decodeByteArray(icon.getImageData(), 0, icon.getImageData().length); // Could not understand custom icon if (bitmap == null) { @@ -306,7 +306,7 @@ public class IconDrawableFactory { bitmap = resize(bitmap); draw = new BitmapDrawable(context.getResources(), bitmap); - customIconMap.put(icon.uuid, draw); + customIconMap.put(icon.getUUID(), draw); } return draw; diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java b/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java index 451018fb4..e80fe7f7e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java @@ -19,13 +19,12 @@ */ package com.kunzisoft.keepass.utils; -import com.kunzisoft.keepass.database.EntrySearchV4; import com.kunzisoft.keepass.database.PwDatabase; import com.kunzisoft.keepass.database.PwDatabaseV4; import com.kunzisoft.keepass.database.PwEntry; import com.kunzisoft.keepass.database.PwEntryV4; -import com.kunzisoft.keepass.database.PwGroupV4; -import com.kunzisoft.keepass.database.SearchParametersV4; +import com.kunzisoft.keepass.database.search.EntrySearchV4; +import com.kunzisoft.keepass.database.search.SearchParametersV4; import java.util.ArrayList; import java.util.List; @@ -158,7 +157,7 @@ public class SprEngineV4 { List list = new ArrayList<>(); // TODO type parameter - EntrySearchV4 entrySearchV4 = new EntrySearchV4((PwGroupV4) ctx.db.getRootGroup()); + EntrySearchV4 entrySearchV4 = new EntrySearchV4(ctx.db.getRootGroup()); entrySearchV4.searchEntries(sp, list); if (list.size() > 0) { diff --git a/app/src/main/res/layout/list_nodes_entry.xml b/app/src/main/res/layout/list_nodes_entry.xml index df0724957..4dc1d2c06 100644 --- a/app/src/main/res/layout/list_nodes_entry.xml +++ b/app/src/main/res/layout/list_nodes_entry.xml @@ -40,6 +40,7 @@ android:id="@+id/entry_text" android:layout_height="wrap_content" android:layout_width="wrap_content" + android:lines="1" style="@style/KeepassDXStyle.TextAppearance.Default" android:layout_centerVertical="true" android:layout_alignParentRight="true" diff --git a/app/src/main/res/layout/search_entry.xml b/app/src/main/res/layout/search_entry.xml new file mode 100644 index 000000000..db60ac2e9 --- /dev/null +++ b/app/src/main/res/layout/search_entry.xml @@ -0,0 +1,51 @@ + + + + + + \ No newline at end of file From 0f22f8af45033210b5d6d61ab1ff6a8406df6b31 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 27 Jul 2018 10:54:58 +0200 Subject: [PATCH 6/7] Add search title --- .../com/kunzisoft/keepass/activities/GroupActivity.java | 7 +++++++ .../kunzisoft/keepass/database/search/SearchDbHelper.java | 2 +- app/src/main/res/layout/list_nodes_with_add_button.xml | 6 ++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 2f54bb21b..a96168bed 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -112,6 +112,7 @@ public class GroupActivity extends LockingActivity private static final String NODE_TO_MOVE_KEY = "NODE_TO_MOVE_KEY"; private Toolbar toolbar; + private View searchTitleView; private ExpandableLayout toolbarPasteExpandableLayout; private Toolbar toolbarPaste; private ImageView iconView; @@ -219,6 +220,7 @@ public class GroupActivity extends LockingActivity iconView = findViewById(R.id.icon); addNodeButtonView = findViewById(R.id.add_node_button); toolbar = findViewById(R.id.toolbar); + searchTitleView = findViewById(R.id.search_title); groupNameView = findViewById(R.id.group_name); toolbarPasteExpandableLayout = findViewById(R.id.expandable_toolbar_paste_layout); toolbarPaste = findViewById(R.id.toolbar_paste); @@ -408,6 +410,11 @@ public class GroupActivity extends LockingActivity } } } + if (currentGroupIsASearch) { + searchTitleView.setVisibility(View.VISIBLE); + } else { + searchTitleView.setVisibility(View.GONE); + } // Assign icon if (currentGroupIsASearch) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java index fd4a7a989..91351aa69 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java @@ -59,7 +59,7 @@ public class SearchDbHelper()); // Search all entries diff --git a/app/src/main/res/layout/list_nodes_with_add_button.xml b/app/src/main/res/layout/list_nodes_with_add_button.xml index cf83ec1f7..7b6cb5bdb 100644 --- a/app/src/main/res/layout/list_nodes_with_add_button.xml +++ b/app/src/main/res/layout/list_nodes_with_add_button.xml @@ -64,6 +64,12 @@ android:layout_alignParentStart="true" android:layout_below="@+id/toolbar" android:orientation="vertical"> + Date: Fri, 27 Jul 2018 21:39:26 +0200 Subject: [PATCH 7/7] Fix search fragment --- .../keepass/activities/GroupActivity.java | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index a96168bed..f0a8a3f70 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -38,6 +38,7 @@ import android.preference.PreferenceManager; import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.support.v7.widget.SearchView; import android.support.v7.widget.Toolbar; @@ -266,9 +267,13 @@ public class GroupActivity extends LockingActivity nodeToMove = null; }); + String fragmentTag = LIST_NODES_FRAGMENT_TAG; + if (currentGroupIsASearch) + fragmentTag = SEARCH_FRAGMENT_TAG; + // Initialize the fragment with the list listNodesFragment = (ListNodesFragment) getSupportFragmentManager() - .findFragmentByTag(LIST_NODES_FRAGMENT_TAG); + .findFragmentByTag(fragmentTag); if (listNodesFragment == null) listNodesFragment = ListNodesFragment.newInstance(mCurrentGroup, readOnly, currentGroupIsASearch); @@ -276,7 +281,7 @@ public class GroupActivity extends LockingActivity getSupportFragmentManager().beginTransaction().replace( R.id.nodes_list_fragment_container, listNodesFragment, - LIST_NODES_FRAGMENT_TAG) + fragmentTag) .commit(); // Add listeners to the add buttons @@ -312,39 +317,47 @@ public class GroupActivity extends LockingActivity } private void openSearchGroup(PwGroup group) { - // if last group was a search, don't add to backstack - openGroup(group, !currentGroupIsASearch, true); + // Delete the previous search fragment + Fragment searchFragment = getSupportFragmentManager().findFragmentByTag(SEARCH_FRAGMENT_TAG); + if (searchFragment != null) { + if ( getSupportFragmentManager() + .popBackStackImmediate(SEARCH_FRAGMENT_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE) ) + getSupportFragmentManager().beginTransaction().remove(searchFragment).commit(); + } + + openGroup(group, true); } private void openChildGroup(PwGroup group) { - openGroup(group, true, false); + openGroup(group, false); } - private void openGroup(PwGroup group, boolean addToBackStack, boolean isASearch) { + private void openGroup(PwGroup group, boolean isASearch) { // Check Timeout if (checkTimeIsAllowedOrFinish(this)) { startRecordTime(this); + // Open a group in a new fragment ListNodesFragment newListNodeFragment = ListNodesFragment.newInstance(group, readOnly, isASearch); FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); // Different animation - if (isASearch) + String fragmentTag; + if (isASearch) { fragmentTransaction.setCustomAnimations(R.anim.slide_in_top, R.anim.slide_out_bottom, R.anim.slide_in_bottom, R.anim.slide_out_top); - else + fragmentTag = SEARCH_FRAGMENT_TAG; + } else { fragmentTransaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right); - - String tag = LIST_NODES_FRAGMENT_TAG; - if (isASearch) - tag = SEARCH_FRAGMENT_TAG; + fragmentTag = LIST_NODES_FRAGMENT_TAG; + } fragmentTransaction.replace(R.id.nodes_list_fragment_container, newListNodeFragment, - tag); - if (addToBackStack) - fragmentTransaction.addToBackStack(LIST_NODES_FRAGMENT_TAG); + fragmentTag); + fragmentTransaction.addToBackStack(fragmentTag); fragmentTransaction.commit(); + listNodesFragment = newListNodeFragment; mCurrentGroup = group; assignGroupViewElements(); @@ -1189,11 +1202,6 @@ public class GroupActivity extends LockingActivity mCurrentGroup = listNodesFragment.getMainGroup(); removeSearchInIntent(getIntent()); assignGroupViewElements(); - - // remove search fragment - Fragment searchFragment = getSupportFragmentManager().findFragmentByTag(SEARCH_FRAGMENT_TAG); - if (searchFragment != null) - getSupportFragmentManager().beginTransaction().remove(searchFragment).commit(); } } }