Implement manual entry of HOTP accounts
This commit is contained in:
parent
c8d47ca3bf
commit
e76b3c3b96
4 changed files with 75 additions and 6 deletions
|
@ -33,6 +33,7 @@ import javax.crypto.spec.SecretKeySpec;
|
||||||
public class TokenCalculator {
|
public class TokenCalculator {
|
||||||
public static final int TOTP_DEFAULT_PERIOD = 30;
|
public static final int TOTP_DEFAULT_PERIOD = 30;
|
||||||
public static final int TOTP_DEFAULT_DIGITS = 6;
|
public static final int TOTP_DEFAULT_DIGITS = 6;
|
||||||
|
public static final int HOTP_INITIAL_COUNTER = 1;
|
||||||
public static final int STEAM_DEFAULT_DIGITS = 5;
|
public static final int STEAM_DEFAULT_DIGITS = 5;
|
||||||
|
|
||||||
private static final char[] STEAMCHARS = new char[] {
|
private static final char[] STEAMCHARS = new char[] {
|
||||||
|
|
|
@ -33,6 +33,7 @@ import android.widget.AdapterView;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
|
|
||||||
import com.github.aakira.expandablelayout.ExpandableLayoutListenerAdapter;
|
import com.github.aakira.expandablelayout.ExpandableLayoutListenerAdapter;
|
||||||
|
@ -57,8 +58,11 @@ public class ManualEntryDialog {
|
||||||
final Spinner typeInput = inputView.findViewById(R.id.manual_type);
|
final Spinner typeInput = inputView.findViewById(R.id.manual_type);
|
||||||
final EditText labelInput = inputView.findViewById(R.id.manual_label);
|
final EditText labelInput = inputView.findViewById(R.id.manual_label);
|
||||||
final EditText secretInput = inputView.findViewById(R.id.manual_secret);
|
final EditText secretInput = inputView.findViewById(R.id.manual_secret);
|
||||||
|
final EditText counterInput = inputView.findViewById(R.id.manual_counter);
|
||||||
final EditText periodInput = inputView.findViewById(R.id.manual_period);
|
final EditText periodInput = inputView.findViewById(R.id.manual_period);
|
||||||
final EditText digitsInput = inputView.findViewById(R.id.manual_digits);
|
final EditText digitsInput = inputView.findViewById(R.id.manual_digits);
|
||||||
|
final LinearLayout counterLayout = inputView.findViewById(R.id.manual_layout_counter);
|
||||||
|
final LinearLayout periodLayout = inputView.findViewById(R.id.manual_layout_period);
|
||||||
final Spinner algorithmInput = inputView.findViewById(R.id.manual_algorithm);
|
final Spinner algorithmInput = inputView.findViewById(R.id.manual_algorithm);
|
||||||
final Button tagsInput = inputView.findViewById(R.id.manual_tags);
|
final Button tagsInput = inputView.findViewById(R.id.manual_tags);
|
||||||
|
|
||||||
|
@ -75,6 +79,7 @@ public class ManualEntryDialog {
|
||||||
|
|
||||||
periodInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_PERIOD));
|
periodInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_PERIOD));
|
||||||
digitsInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_DIGITS));
|
digitsInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_DIGITS));
|
||||||
|
counterInput.setText(String.format(Locale.US, "%d", TokenCalculator.HOTP_INITIAL_COUNTER));
|
||||||
|
|
||||||
typeInput.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
typeInput.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -82,6 +87,9 @@ public class ManualEntryDialog {
|
||||||
Entry.OTPType type = (Entry.OTPType) adapterView.getSelectedItem();
|
Entry.OTPType type = (Entry.OTPType) adapterView.getSelectedItem();
|
||||||
|
|
||||||
if (type == Entry.OTPType.STEAM) {
|
if (type == Entry.OTPType.STEAM) {
|
||||||
|
counterLayout.setVisibility(View.GONE);
|
||||||
|
periodLayout.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
digitsInput.setText(String.format(Locale.US, "%d", TokenCalculator.STEAM_DEFAULT_DIGITS));
|
digitsInput.setText(String.format(Locale.US, "%d", TokenCalculator.STEAM_DEFAULT_DIGITS));
|
||||||
periodInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_PERIOD));
|
periodInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_PERIOD));
|
||||||
algorithmInput.setSelection(algorithmAdapter.getPosition(TokenCalculator.HashAlgorithm.SHA1));
|
algorithmInput.setSelection(algorithmAdapter.getPosition(TokenCalculator.HashAlgorithm.SHA1));
|
||||||
|
@ -89,7 +97,18 @@ public class ManualEntryDialog {
|
||||||
digitsInput.setEnabled(false);
|
digitsInput.setEnabled(false);
|
||||||
periodInput.setEnabled(false);
|
periodInput.setEnabled(false);
|
||||||
algorithmInput.setEnabled(false);
|
algorithmInput.setEnabled(false);
|
||||||
} else {
|
} else if (type == Entry.OTPType.TOTP) {
|
||||||
|
counterLayout.setVisibility(View.GONE);
|
||||||
|
periodLayout.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
digitsInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_DIGITS));
|
||||||
|
digitsInput.setEnabled(true);
|
||||||
|
periodInput.setEnabled(true);
|
||||||
|
algorithmInput.setEnabled(true);
|
||||||
|
} else if (type == Entry.OTPType.HOTP) {
|
||||||
|
counterLayout.setVisibility(View.VISIBLE);
|
||||||
|
periodLayout.setVisibility(View.GONE);
|
||||||
|
|
||||||
digitsInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_DIGITS));
|
digitsInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_DIGITS));
|
||||||
digitsInput.setEnabled(true);
|
digitsInput.setEnabled(true);
|
||||||
periodInput.setEnabled(true);
|
periodInput.setEnabled(true);
|
||||||
|
@ -166,11 +185,12 @@ public class ManualEntryDialog {
|
||||||
Entry.OTPType type = (Entry.OTPType) typeInput.getSelectedItem();
|
Entry.OTPType type = (Entry.OTPType) typeInput.getSelectedItem();
|
||||||
TokenCalculator.HashAlgorithm algorithm = (TokenCalculator.HashAlgorithm) algorithmInput.getSelectedItem();
|
TokenCalculator.HashAlgorithm algorithm = (TokenCalculator.HashAlgorithm) algorithmInput.getSelectedItem();
|
||||||
|
|
||||||
|
String label = labelInput.getText().toString();
|
||||||
|
String secret = secretInput.getText().toString();
|
||||||
|
int digits = Integer.parseInt(digitsInput.getText().toString());
|
||||||
|
|
||||||
if (type == Entry.OTPType.TOTP || type == Entry.OTPType.STEAM) {
|
if (type == Entry.OTPType.TOTP || type == Entry.OTPType.STEAM) {
|
||||||
String label = labelInput.getText().toString();
|
|
||||||
String secret = secretInput.getText().toString();
|
|
||||||
int period = Integer.parseInt(periodInput.getText().toString());
|
int period = Integer.parseInt(periodInput.getText().toString());
|
||||||
int digits = Integer.parseInt(digitsInput.getText().toString());
|
|
||||||
|
|
||||||
Entry e = new Entry(type, secret, period, digits, label, algorithm, tagsAdapter.getActiveTags());
|
Entry e = new Entry(type, secret, period, digits, label, algorithm, tagsAdapter.getActiveTags());
|
||||||
e.updateOTP();
|
e.updateOTP();
|
||||||
|
@ -178,6 +198,13 @@ public class ManualEntryDialog {
|
||||||
adapter.saveEntries();
|
adapter.saveEntries();
|
||||||
|
|
||||||
callingActivity.refreshTags();
|
callingActivity.refreshTags();
|
||||||
|
} else if (type == Entry.OTPType.HOTP) {
|
||||||
|
long counter = Long.parseLong(counterInput.getText().toString());
|
||||||
|
|
||||||
|
Entry e = new Entry(type, secret, counter, digits, label, algorithm, tagsAdapter.getActiveTags());
|
||||||
|
e.updateOTP();
|
||||||
|
adapter.addEntry(e);
|
||||||
|
adapter.saveEntries();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -204,11 +231,25 @@ public class ManualEntryDialog {
|
||||||
@Override
|
@Override
|
||||||
public void afterTextChanged(Editable editable) {
|
public void afterTextChanged(Editable editable) {
|
||||||
if (TextUtils.isEmpty(labelInput.getText()) || TextUtils.isEmpty(secretInput.getText())
|
if (TextUtils.isEmpty(labelInput.getText()) || TextUtils.isEmpty(secretInput.getText())
|
||||||
|| TextUtils.isEmpty(periodInput.getText()) || Integer.parseInt(periodInput.getText().toString()) == 0
|
|
||||||
|| TextUtils.isEmpty(digitsInput.getText()) || Integer.parseInt(digitsInput.getText().toString()) == 0) {
|
|| TextUtils.isEmpty(digitsInput.getText()) || Integer.parseInt(digitsInput.getText().toString()) == 0) {
|
||||||
positiveButton.setEnabled(false);
|
positiveButton.setEnabled(false);
|
||||||
} else {
|
} else {
|
||||||
positiveButton.setEnabled(true);
|
Entry.OTPType type = (Entry.OTPType) typeInput.getSelectedItem();
|
||||||
|
if (type == Entry.OTPType.HOTP) {
|
||||||
|
if (TextUtils.isEmpty(counterInput.getText())) {
|
||||||
|
positiveButton.setEnabled(false);
|
||||||
|
} else {
|
||||||
|
positiveButton.setEnabled(true);
|
||||||
|
}
|
||||||
|
} else if (type == Entry.OTPType.TOTP || type == Entry.OTPType.STEAM) {
|
||||||
|
if (TextUtils.isEmpty(periodInput.getText()) || Integer.parseInt(periodInput.getText().toString()) == 0) {
|
||||||
|
positiveButton.setEnabled(false);
|
||||||
|
} else {
|
||||||
|
positiveButton.setEnabled(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
positiveButton.setEnabled(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -217,5 +258,6 @@ public class ManualEntryDialog {
|
||||||
secretInput.addTextChangedListener(watcher);
|
secretInput.addTextChangedListener(watcher);
|
||||||
periodInput.addTextChangedListener(watcher);
|
periodInput.addTextChangedListener(watcher);
|
||||||
digitsInput.addTextChangedListener(watcher);
|
digitsInput.addTextChangedListener(watcher);
|
||||||
|
counterInput.addTextChangedListener(watcher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,30 @@
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/manual_layout_counter"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:visibility="gone" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_weight="3"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/label_counter"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/manual_counter"
|
||||||
|
android:layout_weight="7"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="number"
|
||||||
|
android:hint="@string/label_counter"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -111,6 +135,7 @@
|
||||||
app:ael_expanded="false">
|
app:ael_expanded="false">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/manual_layout_period"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
<string name="label_secret">Secret</string>
|
<string name="label_secret">Secret</string>
|
||||||
<string name="label_period">Period</string>
|
<string name="label_period">Period</string>
|
||||||
<string name="label_digits">Digits</string>
|
<string name="label_digits">Digits</string>
|
||||||
|
<string name="label_counter">Counter</string>
|
||||||
<string name="label_label">Label</string>
|
<string name="label_label">Label</string>
|
||||||
<string name="label_algorithm">Algorithm</string>
|
<string name="label_algorithm">Algorithm</string>
|
||||||
<string name="label_tags">Tags</string>
|
<string name="label_tags">Tags</string>
|
||||||
|
|
Loading…
Reference in a new issue