From 301476ff5cc6a8a0af102b802f3d80fff23dbfed Mon Sep 17 00:00:00 2001 From: Alexander Bakker Date: Sat, 2 May 2020 18:51:33 +0200 Subject: [PATCH] Write entries to the vault directly in EditEntryActivity This makes it so that EditEntryActivity directly saves entries to the vault, instead of passing them back to MainActivity through an Intent first. This prevents crashes that can occur when an entry has a large icon and the Bundle inside the Intent becomes too large. This is the first part of a series of patches I plan on submitting, where I try to repair the damage done by my misguided obsession of only touching the global state in certain places. --- .../aegis/ui/AegisActivity.java | 17 +++- .../aegis/ui/EditEntryActivity.java | 60 +++++++++---- .../aegis/ui/MainActivity.java | 84 ++++++------------- .../aegis/ui/views/EntryAdapter.java | 21 ++++- .../aegis/ui/views/EntryListView.java | 12 ++- .../aegis/vault/VaultManager.java | 6 ++ 6 files changed, 116 insertions(+), 84 deletions(-) diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/AegisActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/AegisActivity.java index bc86ef18..e5fccc50 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/AegisActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/AegisActivity.java @@ -8,13 +8,14 @@ import android.provider.Settings; import android.view.WindowManager; import android.widget.Toast; +import androidx.annotation.CallSuper; +import androidx.appcompat.app.AppCompatActivity; + import com.beemdevelopment.aegis.AegisApplication; import com.beemdevelopment.aegis.Preferences; import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.Theme; - -import androidx.annotation.CallSuper; -import androidx.appcompat.app.AppCompatActivity; +import com.beemdevelopment.aegis.vault.VaultManagerException; import java.util.Locale; @@ -126,6 +127,16 @@ public abstract class AegisActivity extends AppCompatActivity implements AegisAp this.getResources().updateConfiguration(config, this.getResources().getDisplayMetrics()); } + protected boolean saveVault() { + try { + getApp().getVaultManager().save(); + return true; + } catch (VaultManagerException e) { + Toast.makeText(this, getString(R.string.saving_error), Toast.LENGTH_LONG).show(); + return false; + } + } + /** * Reports whether this Activity has been resumed. (i.e. onResume was called) */ diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/EditEntryActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/EditEntryActivity.java index 3319028a..34ccecb8 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/EditEntryActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/EditEntryActivity.java @@ -31,7 +31,6 @@ import androidx.appcompat.app.AlertDialog; import com.amulyakhare.textdrawable.TextDrawable; import com.avito.android.krop.KropView; import com.beemdevelopment.aegis.R; -import com.beemdevelopment.aegis.vault.VaultEntry; import com.beemdevelopment.aegis.encoding.Base32; import com.beemdevelopment.aegis.encoding.EncodingException; import com.beemdevelopment.aegis.helpers.EditTextHelper; @@ -43,16 +42,18 @@ import com.beemdevelopment.aegis.otp.OtpInfoException; import com.beemdevelopment.aegis.otp.SteamInfo; import com.beemdevelopment.aegis.otp.TotpInfo; import com.beemdevelopment.aegis.util.Cloner; +import com.beemdevelopment.aegis.vault.VaultEntry; +import com.beemdevelopment.aegis.vault.VaultManager; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.transition.Transition; import java.io.ByteArrayOutputStream; -import java.text.Collator; import java.util.ArrayList; import java.util.List; import java.util.TreeSet; +import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; import de.hdodenhof.circleimageview.CircleImageView; @@ -89,22 +90,28 @@ public class EditEntryActivity extends AegisActivity { private RelativeLayout _advancedSettingsHeader; private RelativeLayout _advancedSettings; + private VaultManager _vault; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_edit_entry); + _vault = getApp().getVaultManager(); + _groups = _vault.getGroups(); + ActionBar bar = getSupportActionBar(); bar.setHomeAsUpIndicator(R.drawable.ic_close); bar.setDisplayHomeAsUpEnabled(true); // retrieve info from the calling activity Intent intent = getIntent(); - _origEntry = (VaultEntry) intent.getSerializableExtra("entry"); - _isNew = intent.getBooleanExtra("isNew", false); - _groups = new TreeSet<>(Collator.getInstance()); - _groups.addAll(intent.getStringArrayListExtra("groups")); - if (_isNew) { + UUID entryUUID = (UUID) intent.getSerializableExtra("entryUUID"); + if (entryUUID != null) { + _origEntry = _vault.getEntryByUUID(entryUUID); + } else { + _origEntry = (VaultEntry) intent.getSerializableExtra("newEntry"); + _isNew = true; setTitle(R.string.add_new_entry); } @@ -185,9 +192,8 @@ public class EditEntryActivity extends AegisActivity { _spinnerType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { - String type = _spinnerType.getSelectedItem().toString(); - - switch (type.toLowerCase()) { + String type = _spinnerType.getSelectedItem().toString().toLowerCase(); + switch (type) { case TotpInfo.ID: case SteamInfo.ID: _rowCounter.setVisibility(View.GONE); @@ -251,7 +257,6 @@ public class EditEntryActivity extends AegisActivity { // automatically open advanced settings since 'Secret' is required. if (_isNew) { openAdvancedSettings(); - setGroup(selectedGroup); } } @@ -264,6 +269,7 @@ public class EditEntryActivity extends AegisActivity { int pos = _groups.contains(groupName) ? _groups.headSet(groupName).size() : -1; _spinnerGroup.setSelection(pos + 1, false); } + private void openAdvancedSettings() { Animation fadeOut = new AlphaAnimation(1, 0); fadeOut.setInterpolator(new AccelerateInterpolator()); @@ -344,7 +350,7 @@ public class EditEntryActivity extends AegisActivity { return; } - finish(entry.get(), false); + addAndFinish(entry.get()); }, (dialog, which) -> super.onBackPressed() ); @@ -361,7 +367,7 @@ public class EditEntryActivity extends AegisActivity { break; case R.id.action_delete: Dialogs.showDeleteEntryDialog(this, (dialog, which) -> { - finish(_origEntry, true); + deleteAndFinish(_origEntry); }); break; case R.id.action_default_icon: @@ -389,12 +395,30 @@ public class EditEntryActivity extends AegisActivity { return true; } - private void finish(VaultEntry entry, boolean delete) { + private void addAndFinish(VaultEntry entry) { + if (_isNew) { + _vault.addEntry(entry); + } else { + _vault.replaceEntry(entry); + } + + saveAndFinish(entry, false); + } + + private void deleteAndFinish(VaultEntry entry) { + _vault.removeEntry(entry); + saveAndFinish(entry, true); + } + + private void saveAndFinish(VaultEntry entry, boolean delete) { Intent intent = new Intent(); - intent.putExtra("entry", entry); + intent.putExtra("entryUUID", entry.getUUID()); intent.putExtra("delete", delete); - setResult(RESULT_OK, intent); - finish(); + + if (saveVault()) { + setResult(RESULT_OK, intent); + finish(); + } } @Override @@ -540,7 +564,7 @@ public class EditEntryActivity extends AegisActivity { return false; } - finish(entry, false); + addAndFinish(entry); return true; } diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java index d8e03ce8..be10533a 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java @@ -51,17 +51,17 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.TreeSet; +import java.util.UUID; public class MainActivity extends AegisActivity implements EntryListView.Listener { // activity request codes private static final int CODE_SCAN = 0; private static final int CODE_ADD_ENTRY = 1; private static final int CODE_EDIT_ENTRY = 2; - private static final int CODE_ENTER_ENTRY = 3; - private static final int CODE_DO_INTRO = 4; - private static final int CODE_DECRYPT = 5; - private static final int CODE_PREFERENCES = 6; - private static final int CODE_SCAN_IMAGE = 7; + private static final int CODE_DO_INTRO = 3; + private static final int CODE_DECRYPT = 4; + private static final int CODE_PREFERENCES = 5; + private static final int CODE_SCAN_IMAGE = 6; // permission request codes private static final int CODE_PERM_CAMERA = 0; @@ -112,7 +112,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene _fabMenu = findViewById(R.id.fab); findViewById(R.id.fab_enter).setOnClickListener(view -> { _fabMenu.collapse(); - startEditProfileActivity(CODE_ENTER_ENTRY, null, true); + startEditEntryActivity(CODE_ADD_ENTRY, null, true); }); findViewById(R.id.fab_scan_image).setOnClickListener(view -> { _fabMenu.collapse(); @@ -179,9 +179,6 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene case CODE_EDIT_ENTRY: onEditEntryResult(resultCode, data); break; - case CODE_ENTER_ENTRY: - onEnterEntryResult(resultCode, data); - break; case CODE_DO_INTRO: onDoIntroResult(resultCode, data); break; @@ -238,50 +235,42 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene } } - private void startEditProfileActivity(int requestCode, VaultEntry entry, boolean isNew) { + private void startEditEntryActivity(int requestCode, VaultEntry entry, boolean isNew) { Intent intent = new Intent(this, EditEntryActivity.class); - intent.putExtra("entry", entry != null ? entry : VaultEntry.getDefault()); - intent.putExtra("isNew", isNew); + if (isNew) { + intent.putExtra("newEntry", entry != null ? entry : VaultEntry.getDefault()); + } else { + intent.putExtra("entryUUID", entry.getUUID()); + } intent.putExtra("selectedGroup", _selectedGroup); - intent.putExtra("groups", new ArrayList<>(_vault.getGroups())); startActivityForResult(intent, requestCode); } private void onScanResult(int resultCode, Intent data) { if (resultCode == RESULT_OK) { VaultEntry entry = (VaultEntry) data.getSerializableExtra("entry"); - startEditProfileActivity(CODE_ADD_ENTRY, entry, true); + startEditEntryActivity(CODE_ADD_ENTRY, entry, true); } } private void onAddEntryResult(int resultCode, Intent data) { if (resultCode == RESULT_OK) { - VaultEntry entry = (VaultEntry) data.getSerializableExtra("entry"); - addEntry(entry); - saveVault(); + UUID entryUUID = (UUID) data.getSerializableExtra("entryUUID"); + VaultEntry entry = _vault.getEntryByUUID(entryUUID); + _entryListView.addEntry(entry); } } private void onEditEntryResult(int resultCode, Intent data) { if (resultCode == RESULT_OK) { - VaultEntry entry = (VaultEntry) data.getSerializableExtra("entry"); - if (data.getBooleanExtra("delete", false)) { - deleteEntry(entry); - } else { - // this profile has been serialized/deserialized and is no longer the same instance it once was - // to deal with this, the replaceEntry functions are used - VaultEntry oldEntry = _vault.replaceEntry(entry); - _entryListView.replaceEntry(oldEntry, entry); - saveVault(); - } - } - } + UUID entryUUID = (UUID) data.getSerializableExtra("entryUUID"); - private void onEnterEntryResult(int resultCode, Intent data) { - if (resultCode == RESULT_OK) { - VaultEntry entry = (VaultEntry) data.getSerializableExtra("entry"); - addEntry(entry); - saveVault(); + if (data.getBooleanExtra("delete", false)) { + _entryListView.removeEntry(entryUUID); + } else { + VaultEntry entry = _vault.getEntryByUUID(entryUUID); + _entryListView.replaceEntry(entryUUID, entry); + } } } @@ -309,7 +298,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene GoogleAuthInfo info = GoogleAuthInfo.parseUri(result.getText()); VaultEntry entry = new VaultEntry(info); - startEditProfileActivity(CODE_ADD_ENTRY, entry, true); + startEditEntryActivity(CODE_ADD_ENTRY, entry, true); } catch (NotFoundException | IOException | ChecksumException | FormatException | GoogleAuthInfoException e) { Toast.makeText(this, getString(R.string.unable_to_read_qrcode), Toast.LENGTH_SHORT).show(); e.printStackTrace(); @@ -359,11 +348,6 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene _entryListView.setGroupFilter(group, true); } - private void addEntry(VaultEntry entry) { - _vault.addEntry(entry); - _entryListView.addEntry(entry); - } - private void onDoIntroResult(int resultCode, Intent data) { if (resultCode == IntroActivity.RESULT_EXCEPTION) { // TODO: user feedback @@ -442,10 +426,9 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene if (info != null) { VaultEntry entry = new VaultEntry(info); - startEditProfileActivity(CODE_ADD_ENTRY, entry, true); + startEditEntryActivity(CODE_ADD_ENTRY, entry, true); } } - } @Override @@ -511,13 +494,6 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene saveVault(); } - private void deleteEntry(VaultEntry entry) { - VaultEntry oldEntry = _vault.removeEntry(entry); - saveVault(); - - _entryListView.removeEntry(oldEntry); - } - @Override public boolean onCreateOptionsMenu(Menu menu) { _menu = menu; @@ -662,14 +638,6 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene startActivityForResult(intent, CODE_DECRYPT); } - private void saveVault() { - try { - _vault.save(); - } catch (VaultManagerException e) { - Toast.makeText(this, getString(R.string.saving_error), Toast.LENGTH_LONG).show(); - } - } - private void updateLockIcon() { // hide the lock icon if the vault is not unlocked if (_menu != null && !_vault.isLocked()) { @@ -784,7 +752,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene return true; case R.id.action_edit: - startEditProfileActivity(CODE_EDIT_ENTRY, _selectedEntries.get(0), false); + startEditEntryActivity(CODE_EDIT_ENTRY, _selectedEntries.get(0), false); mode.finish(); return true; diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryAdapter.java b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryAdapter.java index f704f934..5edd546e 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryAdapter.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryAdapter.java @@ -10,12 +10,12 @@ import androidx.recyclerview.widget.RecyclerView; import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.SortCategory; import com.beemdevelopment.aegis.ViewMode; -import com.beemdevelopment.aegis.vault.VaultEntry; import com.beemdevelopment.aegis.helpers.ItemTouchHelperAdapter; import com.beemdevelopment.aegis.otp.HotpInfo; import com.beemdevelopment.aegis.otp.OtpInfo; import com.beemdevelopment.aegis.otp.OtpInfoException; import com.beemdevelopment.aegis.otp.TotpInfo; +import com.beemdevelopment.aegis.vault.VaultEntry; import java.util.ArrayList; import java.util.Collections; @@ -23,6 +23,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; public class EntryAdapter extends RecyclerView.Adapter implements ItemTouchHelperAdapter { private EntryListView _view; @@ -144,6 +145,11 @@ public class EntryAdapter extends RecyclerView.Adapter implements I checkPeriodUniformity(); } + public void removeEntry(UUID uuid) { + VaultEntry entry = getEntryByUUID(uuid); + removeEntry(entry); + } + public void clearEntries() { _entries.clear(); _shownEntries.clear(); @@ -151,7 +157,8 @@ public class EntryAdapter extends RecyclerView.Adapter implements I checkPeriodUniformity(); } - public void replaceEntry(VaultEntry oldEntry, VaultEntry newEntry) { + public void replaceEntry(UUID uuid, VaultEntry newEntry) { + VaultEntry oldEntry = getEntryByUUID(uuid); _entries.set(_entries.indexOf(oldEntry), newEntry); if (_shownEntries.contains(oldEntry)) { @@ -174,6 +181,16 @@ public class EntryAdapter extends RecyclerView.Adapter implements I checkPeriodUniformity(); } + private VaultEntry getEntryByUUID(UUID uuid) { + for (VaultEntry entry : _entries) { + if (entry.getUUID().equals(uuid)) { + return entry; + } + } + + return null; + } + private boolean isEntryFiltered(VaultEntry entry) { String group = entry.getGroup(); String issuer = entry.getIssuer().toLowerCase(); diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java index a9e6cab6..31bc4fcc 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java @@ -22,10 +22,10 @@ import androidx.recyclerview.widget.RecyclerView; import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.SortCategory; import com.beemdevelopment.aegis.ViewMode; -import com.beemdevelopment.aegis.vault.VaultEntry; import com.beemdevelopment.aegis.helpers.SimpleItemTouchHelperCallback; import com.beemdevelopment.aegis.helpers.UiRefresher; import com.beemdevelopment.aegis.otp.TotpInfo; +import com.beemdevelopment.aegis.vault.VaultEntry; import com.bumptech.glide.Glide; import com.bumptech.glide.ListPreloader; import com.bumptech.glide.RequestBuilder; @@ -35,6 +35,7 @@ import com.bumptech.glide.util.ViewPreloadSizeProvider; import java.util.Collections; import java.util.List; +import java.util.UUID; public class EntryListView extends Fragment implements EntryAdapter.Listener { private EntryAdapter _adapter; @@ -262,12 +263,17 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener { updateEmptyState(); } + public void removeEntry(UUID uuid) { + _adapter.removeEntry(uuid); + updateEmptyState(); + } + public void clearEntries() { _adapter.clearEntries(); } - public void replaceEntry(VaultEntry oldEntry, VaultEntry newEntry) { - _adapter.replaceEntry(oldEntry, newEntry); + public void replaceEntry(UUID uuid, VaultEntry newEntry) { + _adapter.replaceEntry(uuid, newEntry); } public void runEntriesAnimation() { diff --git a/app/src/main/java/com/beemdevelopment/aegis/vault/VaultManager.java b/app/src/main/java/com/beemdevelopment/aegis/vault/VaultManager.java index 2157dfe7..4f34be77 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/vault/VaultManager.java +++ b/app/src/main/java/com/beemdevelopment/aegis/vault/VaultManager.java @@ -17,6 +17,7 @@ import java.io.OutputStream; import java.text.Collator; import java.util.Collection; import java.util.TreeSet; +import java.util.UUID; public class VaultManager { public static final String FILENAME = "aegis.json"; @@ -144,6 +145,11 @@ public class VaultManager { _vault.getEntries().add(entry); } + public VaultEntry getEntryByUUID(UUID uuid) { + assertState(false, true); + return _vault.getEntries().getByUUID(uuid); + } + public VaultEntry removeEntry(VaultEntry entry) { assertState(false, true); return _vault.getEntries().remove(entry);