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);