Make digits, period and counter editable for existing entries

Fixes #694
This commit is contained in:
Jakob Nixdorf 2021-03-12 20:48:14 +01:00
parent 7b3fd73742
commit 9430463ac2
No known key found for this signature in database
GPG key ID: BE99BF86574A7DBC
4 changed files with 124 additions and 113 deletions

View file

@ -378,7 +378,7 @@ public class MainActivity extends BaseActivity
} else if (intentAction.equals(Intent.ACTION_VIEW)) {
try {
Entry entry = new Entry(callingIntent.getDataString());
entry.updateOTP();
entry.updateOTP(false);
entry.setLastUsed(System.currentTimeMillis());
adapter.addEntry(entry);
Toast.makeText(this, R.string.toast_intent_creation_succeeded, Toast.LENGTH_LONG).show();
@ -909,7 +909,7 @@ public class MainActivity extends BaseActivity
if(!TextUtils.isEmpty(result)) {
try {
Entry e = new Entry(result);
e.updateOTP();
e.updateOTP(false);
e.setLastUsed(System.currentTimeMillis());
adapter.addEntry(e);
refreshTags();

View file

@ -372,6 +372,10 @@ public class Entry {
return period;
}
public void setPeriod(int period) {
this.period = period;
}
public long getCounter() {
return counter;
}
@ -384,6 +388,10 @@ public class Entry {
return digits;
}
public void setDigits(int digits) {
this.digits = digits;
}
public List<String> getTags() { return tags; }
public void setTags(List<String> tags) { this.tags = tags; }
@ -444,12 +452,12 @@ public class Entry {
listId = newId;
}
public boolean updateOTP() {
public boolean updateOTP(boolean force) {
if (type == OTPType.TOTP || type == OTPType.STEAM) {
long time = System.currentTimeMillis() / 1000;
long counter = time / this.getPeriod();
if (counter > last_update) {
if (force || counter > last_update) {
if (type == OTPType.TOTP)
currentOTP = TokenCalculator.TOTP_RFC6238(secret, period, digits, algorithm);
else if (type == OTPType.STEAM)
@ -474,7 +482,7 @@ public class Entry {
* Checks if the OTP is expiring. The color for the entry will be changed to red if the expiry time is less than or equal to 8 seconds
* COLOR_DEFAULT indicates that the OTP has not expired. In this case check if the OTP is about to expire. Update color to COLOR_RED if it's about to expire
* COLOR_RED indicates that the OTP is already about to expire. Don't check again.
* The color will be reset to COLOR_DEFAULT in {@link #updateOTP()} method
* The color will be reset to COLOR_DEFAULT in {@link #updateOTP(boolean force)} method
*
* @return Return true only if the color has changed to red to save from unnecessary notifying dataset
* */

View file

@ -23,8 +23,9 @@
package org.shadowice.flocke.andotp.Dialogs;
import android.app.AlertDialog;
import android.content.DialogInterface;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.content.res.ResourcesCompat;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
@ -56,10 +57,10 @@ import java.util.concurrent.Callable;
public class ManualEntryDialog {
public static void show(final MainActivity callingActivity, Settings settings, final EntriesCardAdapter adapter) {
show(callingActivity, settings, adapter, null);
show(callingActivity, settings, adapter, null, null);
}
public static void show(final MainActivity callingActivity, Settings settings, final EntriesCardAdapter adapter, Entry oldEntry) {
public static void show(final MainActivity callingActivity, Settings settings, final EntriesCardAdapter adapter, Entry oldEntry, UpdateCallback updateCallback) {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
boolean isNewEntry = oldEntry == null;
@ -112,17 +113,17 @@ public class ManualEntryDialog {
counterLayout.setVisibility(View.GONE);
periodLayout.setVisibility(View.VISIBLE);
if (isNewEntry)
digitsInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_DIGITS));
digitsInput.setEnabled(isNewEntry);
periodInput.setEnabled(isNewEntry);
algorithmInput.setEnabled(isNewEntry);
} else if (type == Entry.OTPType.HOTP) {
counterLayout.setVisibility(View.VISIBLE);
periodLayout.setVisibility(View.GONE);
if (isNewEntry)
digitsInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_DIGITS));
digitsInput.setEnabled(isNewEntry);
periodInput.setEnabled(isNewEntry);
algorithmInput.setEnabled(isNewEntry);
}
}
@ -140,9 +141,7 @@ public class ManualEntryDialog {
}
final TagsAdapter tagsAdapter = new TagsAdapter(callingActivity, tagsHashMap);
final Callable tagsCallable = new Callable() {
@Override
public Object call() throws Exception {
final Callable<?> tagsCallable = () -> {
List<String> selectedTags = tagsAdapter.getActiveTags();
StringBuilder stringBuilder = new StringBuilder();
for(int j = 0; j < selectedTags.size(); j++) {
@ -153,29 +152,18 @@ public class ManualEntryDialog {
}
tagsInput.setText(stringBuilder.toString());
return null;
}
};
tagsInput.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
TagsDialog.show(callingActivity, tagsAdapter, tagsCallable, tagsCallable);
}
});
tagsInput.setOnClickListener(view -> TagsDialog.show(callingActivity, tagsAdapter, tagsCallable, tagsCallable));
final Button expandButton = inputView.findViewById(R.id.dialog_expand_button);
// Dirty fix for the compound drawable to avoid crashes on KitKat
expandButton.setCompoundDrawablesWithIntrinsicBounds(null, null, callingActivity.getResources().getDrawable(R.drawable.ic_arrow_down_accent), null);
expandButton.setCompoundDrawablesWithIntrinsicBounds(null, null, ResourcesCompat.getDrawable(callingActivity.getResources(), R.drawable.ic_arrow_down_accent, null), null);
final ExpandableLinearLayout expandLayout = inputView.findViewById(R.id.dialog_expand_layout);
expandButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
expandLayout.toggle();
}
});
expandButton.setOnClickListener(view -> expandLayout.toggle());
expandLayout.setListener(new ExpandableLayoutListenerAdapter() {
@Override
@ -195,19 +183,14 @@ public class ManualEntryDialog {
builder.setTitle(R.string.dialog_title_manual_entry)
.setView(inputView)
.setPositiveButton(R.string.button_save, null)
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {}
});
.setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> {});
AlertDialog dialog = builder.create();
dialog.show();
final Button positiveButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
positiveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
positiveButton.setOnClickListener(view -> {
//Replace spaces with empty characters
String secret = secretInput.getText().toString().replaceAll("\\s+","");
@ -226,41 +209,50 @@ public class ManualEntryDialog {
if (type == Entry.OTPType.TOTP || type == Entry.OTPType.STEAM) {
int period = Integer.parseInt(periodInput.getText().toString());
if (oldEntry == null) {
if (isNewEntry) {
Entry e = new Entry(type, secret, period, digits, issuer, label, algorithm, tagsAdapter.getActiveTags());
e.updateOTP();
e.updateOTP(false);
e.setLastUsed(System.currentTimeMillis());
adapter.addEntry(e);
} else {
oldEntry.setIssuer(issuer, true);
oldEntry.setLabel(label);
oldEntry.setDigits(digits);
oldEntry.setPeriod(period);
oldEntry.setTags(tagsAdapter.getActiveTags());
adapter.saveAndRefresh(settings.getAutoBackupEncryptedFullEnabled());
oldEntry.updateOTP(true);
if (updateCallback != null)
updateCallback.onUpdate();
}
callingActivity.refreshTags();
} else if (type == Entry.OTPType.HOTP) {
long counter = Long.parseLong(counterInput.getText().toString());
if (oldEntry == null) {
if (isNewEntry) {
Entry e = new Entry(type, secret, counter, digits, issuer, label, algorithm, tagsAdapter.getActiveTags());
e.updateOTP();
e.updateOTP(false);
e.setLastUsed(System.currentTimeMillis());
adapter.addEntry(e);
} else {
oldEntry.setIssuer(issuer, true);
oldEntry.setLabel(label);
oldEntry.setDigits(digits);
oldEntry.setCounter(counter);
oldEntry.setTags(tagsAdapter.getActiveTags());
adapter.saveAndRefresh(settings.getAutoBackupEncryptedFullEnabled());
oldEntry.updateOTP(true);
if (updateCallback != null)
updateCallback.onUpdate();
}
}
dialog.dismiss();
}
});
positiveButton.setEnabled(false);
@ -325,13 +317,13 @@ public class ManualEntryDialog {
issuerInput.setText(oldEntry.getIssuer());
labelInput.setText(oldEntry.getLabel());
secretView.setText(oldEntry.getSecretEncoded());
digitsInput.setText(Integer.toString(oldEntry.getDigits()));
digitsInput.setText(String.format(Locale.ENGLISH ,"%d", oldEntry.getDigits()));
algorithmInput.setSelection(algorithmAdapter.getPosition(oldEntry.getAlgorithm()));
if (oldType == Entry.OTPType.TOTP || oldType == Entry.OTPType.STEAM) {
periodInput.setText(Integer.toString(oldEntry.getPeriod()));
periodInput.setText(String.format(Locale.ENGLISH, "%d", oldEntry.getPeriod()));
} else if (oldType == Entry.OTPType.HOTP) {
counterInput.setText(Long.toString(oldEntry.getCounter()));
counterInput.setText(String.format(Locale.ENGLISH, "%d", oldEntry.getCounter()));
}
for(String tag: oldEntry.getTags()) {
@ -352,10 +344,13 @@ public class ManualEntryDialog {
secretView.setTextColor(secretInput.getTextColors().getColorForState(secretInput.getDrawableState(), R.color.colorPrimary));
typeInput.setEnabled(false);
digitsInput.setEnabled(false);
algorithmInput.setEnabled(false);
periodInput.setEnabled(false);
counterInput.setEnabled(false);
digitsInput.setEnabled(oldType != Entry.OTPType.STEAM);
periodInput.setEnabled(oldType != Entry.OTPType.STEAM);
}
}
public interface UpdateCallback {
void onUpdate();
}
}

View file

@ -126,8 +126,12 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
}
public void saveAndRefresh(boolean auto_backup) {
saveAndRefresh(auto_backup, RecyclerView.NO_POSITION);
}
public void saveAndRefresh(boolean auto_backup, int itemPos) {
updateTagsFilter();
entriesChanged();
entriesChanged(itemPos);
saveEntries(auto_backup);
}
@ -143,10 +147,14 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
return entries.indexOf(displayedEntries.get(displayPosition));
}
private void entriesChanged() {
private void entriesChanged(int itemPos) {
displayedEntries = entries.getEntriesSorted(sortMode);
filterByTags(tagsFilter);
if (itemPos == RecyclerView.NO_POSITION)
notifyDataSetChanged();
else
notifyItemChanged(itemPos);
}
public void updateTagsFilter() {
@ -201,7 +209,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
ArrayList<Entry> newEntries = DatabaseHelper.loadDatabase(context, encryptionKey);
entries.updateEntries(newEntries, true);
entriesChanged();
entriesChanged(RecyclerView.NO_POSITION);
}
}
@ -219,7 +227,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
if (e.isTimeBased()) {
boolean cardVisible = !settings.getTapToReveal() || e.isVisible();
boolean item_changed = e.updateOTP();
boolean item_changed = e.updateOTP(false);
boolean color_changed = false;
// Check color change only if highlighting token feature is enabled and the entry is visible
@ -240,7 +248,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
Entry entry = displayedEntries.get(i);
if (!entry.isTimeBased())
entry.updateOTP();
entry.updateOTP(false);
if(settings.isHighlightTokenOptionEnabled())
entryViewHolder.updateColor(entry.getColor());
@ -372,11 +380,11 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
long counter = entry.getCounter() + 1;
entry.setCounter(counter);
entry.updateOTP();
entry.updateOTP(false);
notifyItemChanged(position);
realEntry.setCounter(counter);
realEntry.updateOTP();
realEntry.updateOTP(false);
saveEntries(settings.getAutoBackupEncryptedFullEnabled());
}
@ -646,7 +654,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
int id = item.getItemId();
if (id == R.id.menu_popup_edit) {
ManualEntryDialog.show((MainActivity) context, settings, EntriesCardAdapter.this, entries.getEntry(getRealIndex(pos)));
ManualEntryDialog.show((MainActivity) context, settings, EntriesCardAdapter.this, entries.getEntry(getRealIndex(pos)), () -> saveAndRefresh(settings.getAutoBackupEncryptedFullEnabled(), pos));
return true;
} else if(id == R.id.menu_popup_changeImage) {
changeThumbnail(pos);
@ -666,7 +674,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
public void setSortMode(SortMode mode) {
this.sortMode = mode;
entriesChanged();
entriesChanged(RecyclerView.NO_POSITION);
}
public SortMode getSortMode() {