Make digits, period and counter editable for existing entries
Fixes #694
This commit is contained in:
parent
7b3fd73742
commit
9430463ac2
4 changed files with 124 additions and 113 deletions
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
* */
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in a new issue