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/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/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 ac682ac09..f0a8a3f70 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,24 +30,31 @@ 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.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; 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; 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; @@ -57,6 +65,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 +78,64 @@ 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 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"; private Toolbar toolbar; - + private View searchTitleView; private ExpandableLayout toolbarPasteExpandableLayout; private Toolbar toolbarPaste; - private ImageView iconView; private AddNodeButtonView addNodeButtonView; + private TextView groupNameView; - 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 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; + + private SearchEntryCursorAdapter searchSuggestionAdapter; + // After a database creation public static void launch(Activity act) { launch(act, READ_ONLY_DEFAULT); @@ -176,39 +201,37 @@ public class GroupActivity extends ListNodesActivity @Override protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + super.onCreate(savedInstanceState); - Log.i(TAG, "Started creating tree"); - if ( mCurrentGroup == null ) { - Log.w(TAG, "Group was null"); - return; - } + 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)); - attachFragmentToContentView(); - + // Initialize views 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); + 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); - 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; - }); + invalidateOptionsMenu(); + + // 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); @@ -223,21 +246,124 @@ public class GroupActivity extends ListNodesActivity } } - addNodeButtonView.setAddGroupClickListener(v -> { - GroupEditDialogFragment.build() - .show(getSupportFragmentManager(), - GroupEditDialogFragment.TAG_CREATE_GROUP); + 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; }); + + String fragmentTag = LIST_NODES_FRAGMENT_TAG; + if (currentGroupIsASearch) + fragmentTag = SEARCH_FRAGMENT_TAG; + + // Initialize the fragment with the list + listNodesFragment = (ListNodesFragment) getSupportFragmentManager() + .findFragmentByTag(fragmentTag); + if (listNodesFragment == null) + listNodesFragment = ListNodesFragment.newInstance(mCurrentGroup, readOnly, currentGroupIsASearch); + + // Attach fragment to content view + getSupportFragmentManager().beginTransaction().replace( + R.id.nodes_list_fragment_container, + listNodesFragment, + fragmentTag) + .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()); } + + // Search suggestion + searchSuggestionAdapter = new SearchEntryCursorAdapter(this, database); + + Log.i(TAG, "Finished creating tree"); } + @Override + protected void onNewIntent(Intent intent) { + setIntent(intent); + if (Intent.ACTION_SEARCH.equals(intent.getAction())) { + // only one instance of search in backstack + openSearchGroup(retrieveCurrentGroup(intent, null)); + currentGroupIsASearch = true; + } else { + currentGroupIsASearch = false; + } + } + + private void openSearchGroup(PwGroup group) { + // 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, false); + } + + 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 + 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); + 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); + fragmentTag = LIST_NODES_FRAGMENT_TAG; + } + + fragmentTransaction.replace(R.id.nodes_list_fragment_container, + newListNodeFragment, + fragmentTag); + fragmentTransaction.addToBackStack(fragmentTag); + fragmentTransaction.commit(); + + listNodesFragment = newListNodeFragment; + mCurrentGroup = group; + assignGroupViewElements(); + } + } + @Override protected void onSaveInstanceState(Bundle outState) { outState.putParcelable(GROUP_ID_KEY, mCurrentGroup.getId()); @@ -246,65 +372,113 @@ public class GroupActivity extends ListNodesActivity outState.putParcelable(NODE_TO_COPY_KEY, nodeToCopy); if (nodeToMove != null) outState.putParcelable(NODE_TO_MOVE_KEY, nodeToMove); + ReadOnlyHelper.onSaveInstanceState(outState, readOnly); 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); + } + + 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(); } } } + if (currentGroupIsASearch) { + searchTitleView.setVisibility(View.VISIBLE); + } else { + searchTitleView.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); + } + } + } + } + + // Show button if allowed + 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(); + } } @Override @@ -313,6 +487,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: + openChildGroup((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: + openChildGroup((PwGroup) node); + break; + case ENTRY: + EntrySelectionHelper.buildResponseWhenEntrySelected(this, (PwEntry) node); + finish(); + break; + } + } else { + switch (node.getType()) { + case GROUP: + openChildGroup((PwGroup) node); + break; + case ENTRY: + EntryActivity.launch(this, (PwEntry) node, readOnly); + break; + } + } + } + } + @Override public boolean onOpenMenuClick(PwNode node) { onNodeClick(node); @@ -485,9 +703,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(); } /** @@ -501,7 +718,7 @@ public class GroupActivity extends ListNodesActivity if (listNodesFragment != null && listNodesFragment.isEmpty()) { if (!PreferencesUtil.isEducationNewNodePerformed(this) - && addNodeButtonView.isVisible()) { + && addNodeButtonView.isEnable()) { TapTargetView.showFor(this, TapTarget.forView(findViewById(R.id.add_button), @@ -652,11 +869,26 @@ 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 + 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); + inflater.inflate(R.menu.default_menu, menu); + super.onCreateOptionsMenu(menu); // Launch education screen @@ -673,7 +905,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 +938,7 @@ public class GroupActivity extends ListNodesActivity return true; case R.id.menu_search: - onSearchRequested(); + //onSearchRequested(); return true; case R.id.menu_lock: @@ -716,8 +948,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 +1024,7 @@ public class GroupActivity extends ListNodesActivity class AfterAddNode extends AfterActionNodeOnFinish { + @Override public void run(PwNode oldNode, PwNode newNode) { super.run(); @@ -807,6 +1043,7 @@ public class GroupActivity extends ListNodesActivity class AfterUpdateNode extends AfterActionNodeOnFinish { + @Override public void run(PwNode oldNode, PwNode newNode) { super.run(); @@ -892,16 +1129,79 @@ public class GroupActivity extends ListNodesActivity } @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/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/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 92% 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..91351aa69 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,31 +54,33 @@ public class SearchDbHelper()); // Search all entries Locale loc = Locale.getDefault(); qStr = qStr.toLowerCase(loc); boolean isOmitBackup = omitBackup(); - + + // TODO Search from the current group Queue 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/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/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/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/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/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 @@ + + + + + + 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_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/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 a942b6643..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 @@ -56,13 +56,43 @@ app:popupTheme="?attr/toolbarPopupAppearance" android:elevation="4dp" tools:targetApi="lollipop"> - + android:layout_below="@+id/toolbar" + android:orientation="vertical"> + + + + + + @@ -70,10 +100,11 @@ + app:layout_behavior="@string/appbar_scrolling_view_behavior" + android:layout_below="@+id/toolbar" + android:background="?android:attr/windowBackground" /> + + + + + \ No newline at end of file