|
|
|
@ -29,6 +29,7 @@ import android.widget.LinearLayout;
|
|
|
|
|
import android.widget.TextView;
|
|
|
|
|
import android.widget.Toast;
|
|
|
|
|
|
|
|
|
|
import androidx.activity.OnBackPressedCallback;
|
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
|
import androidx.appcompat.app.AlertDialog;
|
|
|
|
|
import androidx.appcompat.view.ActionMode;
|
|
|
|
@ -47,11 +48,11 @@ import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
|
|
|
|
|
import com.beemdevelopment.aegis.ui.fragments.preferences.BackupsPreferencesFragment;
|
|
|
|
|
import com.beemdevelopment.aegis.ui.fragments.preferences.PreferencesFragment;
|
|
|
|
|
import com.beemdevelopment.aegis.ui.tasks.QrDecodeTask;
|
|
|
|
|
import com.beemdevelopment.aegis.ui.views.EntryHolder;
|
|
|
|
|
import com.beemdevelopment.aegis.ui.views.EntryListView;
|
|
|
|
|
import com.beemdevelopment.aegis.vault.VaultEntry;
|
|
|
|
|
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
|
|
|
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
|
|
|
|
import com.google.common.base.Strings;
|
|
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Collections;
|
|
|
|
@ -81,12 +82,10 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|
|
|
|
private boolean _isDoingIntro;
|
|
|
|
|
private boolean _isAuthenticating;
|
|
|
|
|
|
|
|
|
|
private String _submittedSearchSubtitle;
|
|
|
|
|
private String _searchQueryInputText;
|
|
|
|
|
private String _activeSearchFilter;
|
|
|
|
|
private String _submittedSearchQuery;
|
|
|
|
|
private String _pendingSearchQuery;
|
|
|
|
|
|
|
|
|
|
private List<VaultEntry> _selectedEntries;
|
|
|
|
|
private ActionMode _actionMode;
|
|
|
|
|
|
|
|
|
|
private Menu _menu;
|
|
|
|
|
private SearchView _searchView;
|
|
|
|
@ -96,8 +95,13 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|
|
|
|
|
|
|
|
|
private FabScrollHelper _fabScrollHelper;
|
|
|
|
|
|
|
|
|
|
private ActionMode _actionMode;
|
|
|
|
|
private ActionMode.Callback _actionModeCallbacks = new ActionModeCallbacks();
|
|
|
|
|
|
|
|
|
|
private LockBackPressHandler _lockBackPressHandler;
|
|
|
|
|
private SearchViewBackPressHandler _searchViewBackPressHandler;
|
|
|
|
|
private ActionModeBackPressHandler _actionModeBackPressHandler;
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
|
|
|
super.onCreate(savedInstanceState);
|
|
|
|
@ -110,13 +114,19 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|
|
|
|
|
|
|
|
|
if (savedInstanceState != null) {
|
|
|
|
|
_isRecreated = true;
|
|
|
|
|
_searchQueryInputText = savedInstanceState.getString("searchQueryInputText");
|
|
|
|
|
_activeSearchFilter = savedInstanceState.getString("activeSearchFilter");
|
|
|
|
|
_submittedSearchSubtitle = savedInstanceState.getString("submittedSearchSubtitle");
|
|
|
|
|
_pendingSearchQuery = savedInstanceState.getString("pendingSearchQuery");
|
|
|
|
|
_submittedSearchQuery = savedInstanceState.getString("submittedSearchQuery");
|
|
|
|
|
_isDoingIntro = savedInstanceState.getBoolean("isDoingIntro");
|
|
|
|
|
_isAuthenticating = savedInstanceState.getBoolean("isAuthenticating");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_lockBackPressHandler = new LockBackPressHandler();
|
|
|
|
|
getOnBackPressedDispatcher().addCallback(this, _lockBackPressHandler);
|
|
|
|
|
_searchViewBackPressHandler = new SearchViewBackPressHandler();
|
|
|
|
|
getOnBackPressedDispatcher().addCallback(this, _searchViewBackPressHandler);
|
|
|
|
|
_actionModeBackPressHandler = new ActionModeBackPressHandler();
|
|
|
|
|
getOnBackPressedDispatcher().addCallback(this, _actionModeBackPressHandler);
|
|
|
|
|
|
|
|
|
|
_entryListView = (EntryListView) getSupportFragmentManager().findFragmentById(R.id.key_profiles);
|
|
|
|
|
_entryListView.setListener(this);
|
|
|
|
|
_entryListView.setCodeGroupSize(_prefs.getCodeGroupSize());
|
|
|
|
@ -178,9 +188,8 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|
|
|
|
@Override
|
|
|
|
|
protected void onSaveInstanceState(@NonNull Bundle instance) {
|
|
|
|
|
super.onSaveInstanceState(instance);
|
|
|
|
|
instance.putString("activeSearchFilter", _activeSearchFilter);
|
|
|
|
|
instance.putString("submittedSearchSubtitle", _submittedSearchSubtitle);
|
|
|
|
|
instance.putString("searchQueryInputText", _searchQueryInputText);
|
|
|
|
|
instance.putString("pendingSearchQuery", _pendingSearchQuery);
|
|
|
|
|
instance.putString("submittedSearchQuery", _submittedSearchQuery);
|
|
|
|
|
instance.putBoolean("isDoingIntro", _isDoingIntro);
|
|
|
|
|
instance.putBoolean("isAuthenticating", _isAuthenticating);
|
|
|
|
|
}
|
|
|
|
@ -619,33 +628,16 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|
|
|
|
checkTimeSyncSetting();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_lockBackPressHandler.setEnabled(
|
|
|
|
|
_vaultManager.isAutoLockEnabled(Preferences.AUTO_LOCK_ON_BACK_BUTTON)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
handleIncomingIntent();
|
|
|
|
|
updateLockIcon();
|
|
|
|
|
doShortcutActions();
|
|
|
|
|
updateErrorBar();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onBackPressed() {
|
|
|
|
|
if (!_searchView.isIconified() || _submittedSearchSubtitle != null) {
|
|
|
|
|
_submittedSearchSubtitle = null;
|
|
|
|
|
_entryListView.setSearchFilter(null);
|
|
|
|
|
_activeSearchFilter = null;
|
|
|
|
|
|
|
|
|
|
collapseSearchView();
|
|
|
|
|
setTitle(R.string.app_name);
|
|
|
|
|
getSupportActionBar().setSubtitle(null);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_vaultManager.isAutoLockEnabled(Preferences.AUTO_LOCK_ON_BACK_BUTTON)) {
|
|
|
|
|
_vaultManager.lock(false);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
super.onBackPressed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void deleteEntries(List<VaultEntry> entries) {
|
|
|
|
|
for (VaultEntry entry: entries) {
|
|
|
|
|
VaultEntry oldEntry = _vaultManager.getVault().removeEntry(entry);
|
|
|
|
@ -669,56 +661,63 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|
|
|
|
|
|
|
|
|
_searchView = (SearchView) searchViewMenuItem.getActionView();
|
|
|
|
|
_searchView.setMaxWidth(Integer.MAX_VALUE);
|
|
|
|
|
_searchView.setOnQueryTextFocusChangeListener((v, hasFocus) -> {
|
|
|
|
|
boolean enabled = _submittedSearchQuery != null || hasFocus;
|
|
|
|
|
_searchViewBackPressHandler.setEnabled(enabled);
|
|
|
|
|
});
|
|
|
|
|
_searchView.setOnCloseListener(() -> {
|
|
|
|
|
boolean enabled = _submittedSearchQuery != null;
|
|
|
|
|
_searchViewBackPressHandler.setEnabled(enabled);
|
|
|
|
|
return false;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
_searchView.setQueryHint(getString(R.string.search));
|
|
|
|
|
if (_prefs.getFocusSearchEnabled() && !_isRecreated) {
|
|
|
|
|
_searchView.setIconified(false);
|
|
|
|
|
_searchView.setFocusable(true);
|
|
|
|
|
_searchView.requestFocus();
|
|
|
|
|
_searchView.requestFocusFromTouch();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public boolean onQueryTextSubmit(String s) {
|
|
|
|
|
setTitle(getString(R.string.search));
|
|
|
|
|
getSupportActionBar().setSubtitle(s);
|
|
|
|
|
_searchQueryInputText = null;
|
|
|
|
|
_submittedSearchSubtitle = s;
|
|
|
|
|
_entryListView.setSearchFilter(s);
|
|
|
|
|
_pendingSearchQuery = null;
|
|
|
|
|
_submittedSearchQuery = s;
|
|
|
|
|
collapseSearchView();
|
|
|
|
|
_searchViewBackPressHandler.setEnabled(true);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean onQueryTextChange(String s) {
|
|
|
|
|
if (_submittedSearchSubtitle == null) {
|
|
|
|
|
if (_submittedSearchQuery == null) {
|
|
|
|
|
_entryListView.setSearchFilter(s);
|
|
|
|
|
_activeSearchFilter = s;
|
|
|
|
|
_searchQueryInputText = s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_pendingSearchQuery = Strings.isNullOrEmpty(s) && !_searchView.isIconified() ? null : s;
|
|
|
|
|
if (_pendingSearchQuery != null) {
|
|
|
|
|
_entryListView.setSearchFilter(_pendingSearchQuery);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
_searchView.setOnSearchClickListener(v -> {
|
|
|
|
|
if (_submittedSearchSubtitle != null) {
|
|
|
|
|
_entryListView.setSearchFilter(null);
|
|
|
|
|
_activeSearchFilter = null;
|
|
|
|
|
_submittedSearchSubtitle = null;
|
|
|
|
|
}
|
|
|
|
|
String query = _submittedSearchQuery != null ? _submittedSearchQuery : _pendingSearchQuery;
|
|
|
|
|
_searchView.setQuery(query, false);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (_submittedSearchSubtitle != null) {
|
|
|
|
|
getSupportActionBar().setSubtitle(_submittedSearchSubtitle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_activeSearchFilter != null) {
|
|
|
|
|
_entryListView.setSearchFilter(_activeSearchFilter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_searchQueryInputText != null) {
|
|
|
|
|
_searchView.setQuery(_searchQueryInputText, false);
|
|
|
|
|
if (_pendingSearchQuery != null) {
|
|
|
|
|
_searchView.setIconified(false);
|
|
|
|
|
_searchView.setQuery(_pendingSearchQuery, false);
|
|
|
|
|
_searchViewBackPressHandler.setEnabled(true);
|
|
|
|
|
} else if (_submittedSearchQuery != null) {
|
|
|
|
|
setTitle(getString(R.string.search));
|
|
|
|
|
getSupportActionBar().setSubtitle(_submittedSearchQuery);
|
|
|
|
|
_entryListView.setSearchFilter(_submittedSearchQuery);
|
|
|
|
|
_searchViewBackPressHandler.setEnabled(true);
|
|
|
|
|
} else if (_prefs.getFocusSearchEnabled() && !_isRecreated) {
|
|
|
|
|
_searchView.setIconified(false);
|
|
|
|
|
_searchView.setFocusable(true);
|
|
|
|
|
_searchView.requestFocus();
|
|
|
|
|
_searchView.requestFocusFromTouch();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
@ -897,7 +896,12 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|
|
|
|
|
|
|
|
|
_selectedEntries.add(entry);
|
|
|
|
|
_entryListView.setActionModeState(true, entry);
|
|
|
|
|
startActionMode();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void startActionMode() {
|
|
|
|
|
_actionMode = startSupportActionMode(_actionModeCallbacks);
|
|
|
|
|
_actionModeBackPressHandler.setEnabled(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@ -975,6 +979,51 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class SearchViewBackPressHandler extends OnBackPressedCallback {
|
|
|
|
|
public SearchViewBackPressHandler() {
|
|
|
|
|
super(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void handleOnBackPressed() {
|
|
|
|
|
if (!_searchView.isIconified() || _submittedSearchQuery != null) {
|
|
|
|
|
_submittedSearchQuery = null;
|
|
|
|
|
_pendingSearchQuery = null;
|
|
|
|
|
_entryListView.setSearchFilter(null);
|
|
|
|
|
|
|
|
|
|
collapseSearchView();
|
|
|
|
|
setTitle(R.string.app_name);
|
|
|
|
|
getSupportActionBar().setSubtitle(null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class LockBackPressHandler extends OnBackPressedCallback {
|
|
|
|
|
public LockBackPressHandler() {
|
|
|
|
|
super(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void handleOnBackPressed() {
|
|
|
|
|
if (_vaultManager.isAutoLockEnabled(Preferences.AUTO_LOCK_ON_BACK_BUTTON)) {
|
|
|
|
|
_vaultManager.lock(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class ActionModeBackPressHandler extends OnBackPressedCallback {
|
|
|
|
|
public ActionModeBackPressHandler() {
|
|
|
|
|
super(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void handleOnBackPressed() {
|
|
|
|
|
if (_actionMode != null) {
|
|
|
|
|
_actionMode.finish();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class ActionModeCallbacks implements ActionMode.Callback {
|
|
|
|
|
@Override
|
|
|
|
|
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
|
|
|
@ -1044,6 +1093,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|
|
|
|
@Override
|
|
|
|
|
public void onDestroyActionMode(ActionMode mode) {
|
|
|
|
|
_entryListView.setActionModeState(false, null);
|
|
|
|
|
_actionModeBackPressHandler.setEnabled(false);
|
|
|
|
|
_selectedEntries.clear();
|
|
|
|
|
_actionMode = null;
|
|
|
|
|
}
|
|
|
|
|