Files
KeePassDX/app/src/main/java/com/keepassdroid/activities/GroupActivity.java
2018-03-22 16:48:02 +01:00

443 lines
16 KiB
Java

/*
* 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 2 of the License, or
* (at your option) any later version.
*
* KeePass DX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.keepassdroid.activities;
import android.app.Activity;
import android.app.Dialog;
import android.app.SearchManager;
import android.app.assist.AssistStructure;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.annotation.RequiresApi;
import android.support.v7.widget.RecyclerView;
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.widget.ImageView;
import com.keepassdroid.adapters.NodeAdapter;
import com.keepassdroid.app.App;
import com.keepassdroid.autofill.AutofillHelper;
import com.keepassdroid.database.Database;
import com.keepassdroid.database.PwEntry;
import com.keepassdroid.database.PwGroup;
import com.keepassdroid.database.PwGroupId;
import com.keepassdroid.database.PwNode;
import com.keepassdroid.database.SortNodeEnum;
import com.keepassdroid.database.edit.AddGroup;
import com.keepassdroid.database.edit.DeleteEntry;
import com.keepassdroid.database.edit.DeleteGroup;
import com.keepassdroid.dialogs.AssignMasterKeyDialogFragment;
import com.keepassdroid.dialogs.GroupEditDialogFragment;
import com.keepassdroid.dialogs.IconPickerDialogFragment;
import com.keepassdroid.dialogs.ReadOnlyDialog;
import com.keepassdroid.search.SearchResultsActivity;
import com.keepassdroid.tasks.ProgressTask;
import com.keepassdroid.view.AddNodeButtonView;
import com.kunzisoft.keepass.R;
public class GroupActivity extends ListNodesActivity
implements GroupEditDialogFragment.EditGroupListener, IconPickerDialogFragment.IconPickerListener {
private static final String GROUP_ID_KEY = "GROUP_ID_KEY";
private AddNodeButtonView addNodeButtonView;
protected boolean addGroupEnabled = false;
protected boolean addEntryEnabled = false;
protected boolean isRoot = false;
protected boolean readOnly = false;
protected EditGroupDialogAction editGroupDialogAction = EditGroupDialogAction.NONE;
private AutofillHelper autofillHelper;
private enum EditGroupDialogAction {
CREATION, UPDATE, NONE
}
private static final String TAG = "Group Activity:";
public static void launch(Activity act) {
LockingActivity.recordFirstTimeBeforeLaunch(act);
launch(act, (PwGroup) null);
}
public static void launch(Activity act, PwGroup group) {
if (LockingActivity.checkTimeIsAllowedOrFinish(act)) {
Intent intent = new Intent(act, GroupActivity.class);
if (group != null) {
intent.putExtra(GROUP_ID_KEY, group.getId());
}
act.startActivityForResult(intent, 0);
}
}
@RequiresApi(api = Build.VERSION_CODES.O)
public static void launch(Activity act, AssistStructure assistStructure) {
if ( assistStructure != null ) {
LockingActivity.recordFirstTimeBeforeLaunch(act);
launch(act, null, assistStructure);
} else {
launch(act);
}
}
@RequiresApi(api = Build.VERSION_CODES.O)
public static void launch(Activity act, PwGroup group, AssistStructure assistStructure) {
if ( assistStructure != null ) {
if (LockingActivity.checkTimeIsAllowedOrFinish(act)) {
Intent intent = new Intent(act, GroupActivity.class);
if (group != null) {
intent.putExtra(GROUP_ID_KEY, group.getId());
}
AutofillHelper.addAssistStructureExtraInIntent(intent, assistStructure);
act.startActivityForResult(intent, AutofillHelper.AUTOFILL_RESPONSE_REQUEST_CODE);
}
} else {
launch(act, group);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.w(TAG, "Retrieved tree");
if ( mCurrentGroup == null ) {
Log.w(TAG, "Group was null");
return;
}
// Construct main view
setContentView(getLayoutInflater().inflate(R.layout.list_nodes_with_add_button, null));
addNodeButtonView = findViewById(R.id.add_node_button);
addNodeButtonView.enableAddGroup(addGroupEnabled);
addNodeButtonView.enableAddEntry(addEntryEnabled);
// Hide when scroll
RecyclerView recyclerView = findViewById(R.id.nodes_list);
recyclerView.addOnScrollListener(addNodeButtonView.hideButtonOnScrollListener());
Toolbar toolbar = findViewById(R.id.toolbar);
toolbar.setTitle("");
setSupportActionBar(toolbar);
if ( mCurrentGroup.getParent() != null )
toolbar.setNavigationIcon(R.drawable.ic_arrow_up_white_24dp);
addNodeButtonView.setAddGroupClickListener(v -> {
editGroupDialogAction = EditGroupDialogAction.CREATION;
GroupEditDialogFragment groupEditDialogFragment = new GroupEditDialogFragment();
groupEditDialogFragment.show(getSupportFragmentManager(),
GroupEditDialogFragment.TAG_CREATE_GROUP);
});
addNodeButtonView.setAddEntryClickListener(v ->
EntryEditActivity.Launch(GroupActivity.this, mCurrentGroup));
setGroupTitle();
setGroupIcon();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
autofillHelper = new AutofillHelper();
autofillHelper.retrieveAssistStructure(getIntent());
}
Log.w(TAG, "Finished creating tree");
if (isRoot) {
showWarnings();
}
}
protected PwGroup initCurrentGroup() {
PwGroup currentGroup;
Database db = App.getDB();
readOnly = db.readOnly;
PwGroup root = db.pm.rootGroup;
Log.w(TAG, "Creating tree view");
PwGroupId pwGroupId = (PwGroupId) getIntent().getSerializableExtra(GROUP_ID_KEY);
if ( pwGroupId == null ) {
currentGroup = root;
} else {
currentGroup = db.pm.groups.get(pwGroupId);
}
if (currentGroup != null) {
addGroupEnabled = !readOnly;
addEntryEnabled = !readOnly; // TODO ReadOnly
isRoot = (currentGroup == root);
if (!currentGroup.allowAddEntryIfIsRoot())
addEntryEnabled = !isRoot && addEntryEnabled;
}
return currentGroup;
}
@Override
protected RecyclerView defineNodeList() {
return (RecyclerView) findViewById(R.id.nodes_list);
}
@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) {
mAdapter.registerANodeToUpdate(node);
switch (node.getType()) {
case GROUP:
GroupActivity.launch(this, (PwGroup) node, assistStructure);
break;
case ENTRY:
// Build response with the entry selected
autofillHelper.buildResponseWhenEntrySelected(this, (PwEntry) node);
finish();
break;
}
}
}
if ( assistStructure == null ){
super.onNodeClick(node);
}
}
@Override
protected void addOptionsToAdapter(NodeAdapter nodeAdapter) {
super.addOptionsToAdapter(nodeAdapter);
nodeAdapter.setActivateContextMenu(true);
nodeAdapter.setNodeMenuListener(new NodeAdapter.NodeMenuListener() {
@Override
public boolean onOpenMenuClick(PwNode node) {
mAdapter.registerANodeToUpdate(node);
switch (node.getType()) {
case GROUP:
GroupActivity.launch(GroupActivity.this, (PwGroup) node);
break;
case ENTRY:
EntryActivity.launch(GroupActivity.this, (PwEntry) node);
break;
}
return true;
}
@Override
public boolean onEditMenuClick(PwNode node) {
mAdapter.registerANodeToUpdate(node);
switch (node.getType()) {
case GROUP:
editGroupDialogAction = EditGroupDialogAction.UPDATE;
GroupEditDialogFragment groupEditDialogFragment =
GroupEditDialogFragment.build(node);
groupEditDialogFragment.show(getSupportFragmentManager(),
GroupEditDialogFragment.TAG_CREATE_GROUP);
break;
case ENTRY:
EntryEditActivity.Launch(GroupActivity.this, (PwEntry) node);
break;
}
return true;
}
@Override
public boolean onDeleteMenuClick(PwNode node) {
switch (node.getType()) {
case GROUP:
deleteGroup((PwGroup) node);
break;
case ENTRY:
deleteEntry((PwEntry) node);
break;
}
return true;
}
});
}
@Override
protected void onResume() {
super.onResume();
// Show button on resume
addNodeButtonView.showButton();
}
@Override
protected void onStop() {
super.onStop();
// Hide button
addNodeButtonView.hideButton();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data);
}
}
@Override
public void onSortSelected(SortNodeEnum sortNodeEnum, boolean ascending, boolean groupsBefore, boolean recycleBinBottom) {
super.onSortSelected(sortNodeEnum, ascending, groupsBefore, recycleBinBottom);
// Show button if hide after sort
addNodeButtonView.showButton();
}
protected void setGroupIcon() {
if (mCurrentGroup != null) {
ImageView iv = (ImageView) findViewById(R.id.icon);
App.getDB().drawFactory.assignDrawableTo(iv, getResources(), mCurrentGroup.getIcon());
}
}
private void deleteEntry(PwEntry entry) {
Handler handler = new Handler();
DeleteEntry task = new DeleteEntry(this, App.getDB(), entry,
new AfterDeleteNode(handler, entry));
ProgressTask pt = new ProgressTask(this, task, R.string.saving_database);
pt.run();
}
private void deleteGroup(PwGroup group) {
//TODO Verify trash recycle bin
Handler handler = new Handler();
DeleteGroup task = new DeleteGroup(this, App.getDB(), group,
new AfterDeleteNode(handler, group));
ProgressTask pt = new ProgressTask(this, task, R.string.saving_database);
pt.run();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.search, menu);
inflater.inflate(R.menu.database_master_key, menu);
inflater.inflate(R.menu.database_lock, menu);
// Get the SearchView and set the searchable configuration
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
assert searchManager != null;
MenuItem searchItem = menu.findItem(R.id.menu_search);
SearchView searchView = null;
if (searchItem != null) {
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.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
case R.id.menu_search:
onSearchRequested();
return true;
case R.id.menu_lock:
lockAndExit();
return true;
case R.id.menu_change_master_key:
setPassword();
return true;
}
return super.onOptionsItemSelected(item);
}
private void setPassword() {
AssignMasterKeyDialogFragment dialog = new AssignMasterKeyDialogFragment();
dialog.show(getSupportFragmentManager(), "passwordDialog");
}
@Override
public void approveEditGroup(Bundle bundle) {
String GroupName = bundle.getString(GroupEditDialogFragment.KEY_NAME);
int GroupIconID = bundle.getInt(GroupEditDialogFragment.KEY_ICON_ID);
switch (editGroupDialogAction) {
case CREATION:
// If edit group creation
Handler handler = new Handler();
AddGroup task = new AddGroup(this, App.getDB(), GroupName, GroupIconID, mCurrentGroup,
new AfterAddNode(handler), false);
ProgressTask pt = new ProgressTask(this, task, R.string.saving_database);
pt.run();
break;
case UPDATE:
// If edit group update
// TODO UpdateGroup
break;
}
editGroupDialogAction = EditGroupDialogAction.NONE;
}
@Override
public void cancelEditGroup(Bundle bundle) {
// Do nothing here
}
@Override
// For icon in create tree dialog
public void iconPicked(Bundle bundle) {
GroupEditDialogFragment groupEditDialogFragment =
(GroupEditDialogFragment) getSupportFragmentManager()
.findFragmentByTag(GroupEditDialogFragment.TAG_CREATE_GROUP);
if (groupEditDialogFragment != null) {
groupEditDialogFragment.iconPicked(bundle);
}
}
protected void showWarnings() {
if (App.getDB().readOnly) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (prefs.getBoolean(getString(R.string.show_read_only_warning), true)) {
Dialog dialog = new ReadOnlyDialog(this);
dialog.show();
}
}
}
}