mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-06-28 19:59:54 +00:00
Add check for duplicates upon saving entry
This commit is contained in:
parent
6f270144e2
commit
1335be6787
5 changed files with 264 additions and 0 deletions
|
@ -1,6 +1,7 @@
|
|||
package com.beemdevelopment.aegis.ui;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
@ -27,6 +28,7 @@ import androidx.activity.result.contract.ActivityResultContracts.StartActivityFo
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.amulyakhare.textdrawable.TextDrawable;
|
||||
import com.avito.android.krop.KropView;
|
||||
|
@ -87,6 +89,7 @@ import java.util.Collections;
|
|||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
@ -852,10 +855,92 @@ public class EditEntryActivity extends AegisActivity {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (_isNew) {
|
||||
for (VaultEntry existing : _vaultManager.getVault().getEntries()) {
|
||||
if (entry.hasSameNameAndIssuer(existing)) {
|
||||
showDuplicateBottomSheet(entry);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addAndFinish(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void showDuplicateBottomSheet(VaultEntry newEntry) {
|
||||
BottomSheetDialog dialog = new BottomSheetDialog(this);
|
||||
View view = getLayoutInflater().inflate(R.layout.dialog_duplicate_entry, null);
|
||||
dialog.setContentView(view);
|
||||
|
||||
dialog.setCancelable(false);
|
||||
|
||||
View overwrite = view.findViewById(R.id.overwrite_entry);
|
||||
View addSuffix = view.findViewById(R.id.create_new_entry);
|
||||
View cancel = view.findViewById(R.id.cancel_save);
|
||||
|
||||
TextView suffixSubtext = view.findViewById(R.id.duplicate_suffix_subtitle);
|
||||
|
||||
String baseName = newEntry.getName();
|
||||
Set<String> existingNames = new HashSet<>();
|
||||
for (VaultEntry e : _vaultManager.getVault().getEntries()) {
|
||||
if (e.getIssuer().equals(newEntry.getIssuer())) {
|
||||
existingNames.add(e.getName());
|
||||
}
|
||||
}
|
||||
|
||||
int counter = 2;
|
||||
String newName;
|
||||
do {
|
||||
newName = baseName + " #" + counter++;
|
||||
} while (existingNames.contains(newName));
|
||||
|
||||
suffixSubtext.setText(getString(R.string.dialog_duplicate_entry_suffix_subtitle, newName));
|
||||
|
||||
overwrite.setOnClickListener(v -> {
|
||||
List<VaultEntry> duplicates = new ArrayList<>();
|
||||
for (VaultEntry existing : _vaultManager.getVault().getEntries()) {
|
||||
if (existing.hasSameNameAndIssuer(newEntry)) {
|
||||
duplicates.add(existing);
|
||||
}
|
||||
}
|
||||
|
||||
Resources res = getResources();
|
||||
String message = res.getQuantityString(
|
||||
R.plurals.dialog_duplicate_entry_overwrite_dialog_message,
|
||||
duplicates.size(),
|
||||
duplicates.size(),
|
||||
newEntry.getIssuer(),
|
||||
newEntry.getName()
|
||||
);
|
||||
|
||||
new MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.dialog_duplicate_entry_overwrite_dialog_title)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(R.string.action_delete, (d, which) -> {
|
||||
for (VaultEntry dup : duplicates) {
|
||||
_vaultManager.getVault().removeEntry(dup);
|
||||
}
|
||||
|
||||
dialog.dismiss();
|
||||
addAndFinish(newEntry);
|
||||
})
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.show();
|
||||
});
|
||||
|
||||
String finalNewName = newName;
|
||||
addSuffix.setOnClickListener(v -> {
|
||||
newEntry.setName(finalNewName);
|
||||
dialog.dismiss();
|
||||
addAndFinish(newEntry);
|
||||
});
|
||||
|
||||
cancel.setOnClickListener(v -> dialog.dismiss());
|
||||
|
||||
Dialogs.showSecureDialog(dialog);
|
||||
}
|
||||
|
||||
private static void setViewEnabled(View view, boolean enabled) {
|
||||
view.setEnabled(enabled);
|
||||
|
||||
|
|
|
@ -239,6 +239,10 @@ public class VaultEntry extends UUIDMap.Value {
|
|||
&& getGroups().equals(entry.getGroups());
|
||||
}
|
||||
|
||||
public boolean hasSameNameAndIssuer(VaultEntry entry) {
|
||||
return getName().equals(entry.getName()) && getIssuer().equals(entry.getIssuer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports whether this entry has its values set to the defaults.
|
||||
*/
|
||||
|
|
9
app/src/main/res/drawable/ic_tag_24.xml
Normal file
9
app/src/main/res/drawable/ic_tag_24.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:pathData="m240,800 l40,-160L120,640l20,-80h160l40,-160L180,400l20,-80h160l40,-160h80l-40,160h160l40,-160h80l-40,160h160l-20,80L660,400l-40,160h160l-20,80L600,640l-40,160h-80l40,-160L360,640l-40,160h-80ZM380,560h160l40,-160L420,400l-40,160Z"
|
||||
android:fillColor="#e3e3e3"/>
|
||||
</vector>
|
151
app/src/main/res/layout/dialog_duplicate_entry.xml
Normal file
151
app/src/main/res/layout/dialog_duplicate_entry.xml
Normal file
|
@ -0,0 +1,151 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:paddingTop="16dp"
|
||||
android:text="@string/dialog_duplicate_entry_title"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/duplicate_warning_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dialog_duplicate_entry_message"
|
||||
android:textSize="16sp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="20dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/overwrite_entry"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingVertical="15dp"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:src="@drawable/ic_outline_brush_24"
|
||||
app:tint="?attr/colorOnSurfaceVariant" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginStart="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dialog_duplicate_entry_overwrite_title"
|
||||
android:textSize="17sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dialog_duplicate_entry_overwrite_subtitle"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?attr/colorOnSurfaceVariant" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/create_new_entry"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingVertical="15dp"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:src="@drawable/ic_tag_24"
|
||||
app:tint="?attr/colorOnSurfaceVariant" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginStart="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dialog_duplicate_entry_suffix_title"
|
||||
android:textSize="17sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/duplicate_suffix_subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dialog_duplicate_entry_suffix_subtitle"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?attr/colorOnSurfaceVariant" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/cancel_save"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingVertical="15dp"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:src="@drawable/ic_outline_close_24"
|
||||
app:tint="?attr/colorOnSurfaceVariant" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginStart="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dialog_duplicate_entry_cancel_title"
|
||||
android:textSize="17sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dialog_duplicate_entry_cancel_subtitle"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?attr/colorOnSurfaceVariant" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
|
@ -373,6 +373,21 @@
|
|||
<string name="note" comment="Users can add a note to an entry">Note</string>
|
||||
<string name="clear">Clear</string>
|
||||
|
||||
<string name="dialog_duplicate_entry_title">Duplicate entry</string>
|
||||
<string name="dialog_duplicate_entry_message">This entry has the same name and issuer as one or more existing entries. How would you like to proceed?</string>
|
||||
<string name="dialog_duplicate_entry_overwrite_title">Overwrite existing entry/entries</string>
|
||||
<string name="dialog_duplicate_entry_overwrite_subtitle">Replace the existing entry or entries with the new one. This action cannot be undone</string>
|
||||
<string name="dialog_duplicate_entry_suffix_title">Add suffix</string>
|
||||
<string name="dialog_duplicate_entry_suffix_subtitle">Add a suffix to the name of this new entry. The new name will be: %s</string>
|
||||
<string name="dialog_duplicate_entry_cancel_title">Cancel save</string>
|
||||
<string name="dialog_duplicate_entry_cancel_subtitle">Allows you to edit the entry before attempting to save it again</string>
|
||||
<plurals name="dialog_duplicate_entry_overwrite_dialog_message">
|
||||
<item quantity="one">Are you sure you want to delete %d entry with the following name:\n\n%s - %s</item>
|
||||
<item quantity="other">Are you sure you want to delete %d entries with the following name:\n\n%s - %s</item>
|
||||
</plurals>
|
||||
|
||||
<string name="dialog_duplicate_entry_overwrite_dialog_title">Confirm deletion</string>
|
||||
|
||||
<string name="pref_haptic_feedback_summary">Make your device vibrate when codes are refreshing</string>
|
||||
<string name="pref_haptic_feedback_title">Haptic feedback</string>
|
||||
<string name="pref_highlight_entry_title">Highlight tokens when tapped</string>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue