Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4013a12
commit d40daba
Showing
9 changed files
with
427 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
android:layout_width="match_parent" | ||
android:layout_height="match_parent" | ||
android:padding="16dp" > | ||
|
||
<TableRow | ||
android:layout_width="match_parent" | ||
android:layout_height="48dp" > | ||
<TextView | ||
android:layout_width="wrap_content" | ||
android:layout_height="match_parent" | ||
android:gravity="center_vertical|right" | ||
android:text="@string/interval" | ||
android:paddingRight="8dp" /> | ||
|
||
<EditText | ||
android:id="@+id/issuer" | ||
android:layout_width="0dp" | ||
android:layout_height="match_parent" | ||
android:layout_weight="1" | ||
android:hint="jdoe@example.com" | ||
android:textAppearance="?android:attr/textAppearanceSmallInverse" /> | ||
</TableRow> | ||
|
||
<TableRow | ||
android:layout_width="match_parent" | ||
android:layout_height="48dp" > | ||
|
||
<TextView | ||
android:layout_width="wrap_content" | ||
android:layout_height="match_parent" | ||
android:gravity="center_vertical|right" | ||
android:text="@string/id" | ||
android:paddingRight="8dp" /> | ||
|
||
<EditText | ||
android:id="@+id/id" | ||
android:layout_width="0dp" | ||
android:layout_height="match_parent" | ||
android:layout_weight="1" | ||
android:hint="18c5d06cfcbd4927" | ||
android:textAppearance="?android:attr/textAppearanceSmallInverse" /> | ||
</TableRow> | ||
|
||
<TableRow | ||
android:layout_width="match_parent" | ||
android:layout_height="48dp" > | ||
|
||
<TextView | ||
android:layout_width="wrap_content" | ||
android:layout_height="match_parent" | ||
android:gravity="center_vertical|right" | ||
android:text="@string/secret" | ||
android:paddingRight="8dp" /> | ||
|
||
<EditText | ||
android:id="@+id/secret" | ||
android:layout_width="0dp" | ||
android:layout_height="match_parent" | ||
android:layout_weight="1" | ||
android:digits="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ234567=" | ||
android:hint="Base32 (ex. 'GEZDGNBV')" | ||
android:textAppearance="?android:attr/textAppearanceSmall" /> | ||
</TableRow> | ||
|
||
<View | ||
android:layout_width="match_parent" | ||
android:layout_height="1dp" | ||
android:layout_marginTop="12dp" | ||
android:layout_marginBottom="4dp" | ||
android:background="?android:attr/dividerHorizontal" | ||
/> | ||
|
||
<TableRow | ||
android:layout_width="match_parent" | ||
android:layout_height="48dp" > | ||
<TextView | ||
android:layout_width="wrap_content" | ||
android:layout_height="match_parent" | ||
android:gravity="center_vertical|right" | ||
android:text="@string/type" | ||
android:paddingRight="8dp" /> | ||
|
||
<Spinner | ||
android:id="@+id/type" | ||
android:layout_width="0dp" | ||
android:layout_height="match_parent" | ||
android:layout_weight="1" | ||
android:entries="@array/token_types" /> | ||
</TableRow> | ||
|
||
<TableRow | ||
android:layout_width="match_parent" | ||
android:layout_height="48dp" > | ||
<TextView | ||
android:layout_width="wrap_content" | ||
android:layout_height="match_parent" | ||
android:gravity="center_vertical|right" | ||
android:text="@string/algorithm" | ||
android:paddingRight="8dp" /> | ||
|
||
<Spinner | ||
android:id="@+id/algorithm" | ||
android:layout_width="0dp" | ||
android:layout_height="match_parent" | ||
android:layout_weight="1" | ||
android:entries="@array/algorithms" /> | ||
</TableRow> | ||
|
||
<TableRow | ||
android:layout_width="match_parent" | ||
android:layout_height="48dp" > | ||
<TextView | ||
android:id="@+id/interval_label" | ||
android:layout_width="wrap_content" | ||
android:layout_height="match_parent" | ||
android:gravity="center_vertical|right" | ||
android:text="@string/interval" | ||
android:paddingRight="8dp" /> | ||
|
||
<EditText | ||
android:id="@+id/interval" | ||
android:layout_width="0dp" | ||
android:layout_height="match_parent" | ||
android:layout_weight="1" | ||
android:inputType="number" | ||
android:text="30" /> | ||
</TableRow> | ||
|
||
<TableRow | ||
android:layout_width="match_parent" | ||
android:layout_height="48dp" > | ||
<TextView | ||
android:layout_width="wrap_content" | ||
android:layout_height="match_parent" | ||
android:gravity="center_vertical|right" | ||
android:text="@string/digits" | ||
android:paddingRight="8dp" /> | ||
|
||
<RadioGroup | ||
android:layout_width="0dp" | ||
android:layout_height="match_parent" | ||
android:layout_weight="1" | ||
android:orientation="horizontal" > | ||
<RadioButton | ||
android:id="@+id/digits6" | ||
android:layout_width="0dp" | ||
android:layout_height="match_parent" | ||
android:layout_weight="1" | ||
android:text="6" | ||
android:checked="true" /> | ||
|
||
<RadioButton | ||
android:id="@+id/digits8" | ||
android:layout_width="0dp" | ||
android:layout_height="match_parent" | ||
android:layout_weight="1" | ||
android:text="8" /> | ||
</RadioGroup> | ||
</TableRow> | ||
</TableLayout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,39 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<resources> | ||
<string name="app_name">FreeOTP</string> | ||
<string name="action_add">Add</string> | ||
<string name="token_scan_invalid">The scanned token data was invalid!</string> | ||
<string name="add">Add</string> | ||
<string name="invalid_token">The token specified was invalid!</string> | ||
<string name="delete_message">Are you sure you want to remove this token?\n\nNOTE: This will NOT disable two-factor authentication on the server.\n\n</string> | ||
<string name="delete">Delete</string> | ||
<string name="yes">Yes</string> | ||
<string name="no">No</string> | ||
<string name="install_title">Install Barcode Scanner?</string> | ||
<string name="install_message">Barcode Scanner is required. Would you like to install it?</string> | ||
<string name="no_keys">No OTP keys installed.</string> | ||
<string name="add_token">Add Token</string> | ||
<string name="scan_qr_code">Scan QR Code</string> | ||
<string name="interval">Interval</string> | ||
<string name="counter">Counter</string> | ||
<string name="id">ID</string> | ||
<string name="secret">Secret</string> | ||
<string name="type">Type</string> | ||
<string name="algorithm">Algorithm</string> | ||
<string name="digits">Digits</string> | ||
|
||
<string-array name="token_types"> | ||
<item>Time-based (TOTP)</item> | ||
<item>Counter-based (HOTP)</item> | ||
</string-array> | ||
|
||
<string-array name="digits"> | ||
<item>6</item> | ||
<item>8</item> | ||
</string-array> | ||
|
||
<string-array name="algorithms"> | ||
<item>MD5</item> | ||
<item>SHA1</item> | ||
<item>SHA256</item> | ||
<item>SHA512</item> | ||
</string-array> | ||
</resources> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package org.fedorahosted.freeotp; | ||
|
||
import java.util.Locale; | ||
|
||
import android.app.AlertDialog; | ||
import android.content.Context; | ||
import android.content.DialogInterface; | ||
import android.net.Uri; | ||
import android.view.View; | ||
import android.widget.AdapterView; | ||
import android.widget.EditText; | ||
import android.widget.RadioButton; | ||
import android.widget.Spinner; | ||
import android.widget.TextView; | ||
|
||
public abstract class AddTokenDialog extends AlertDialog { | ||
private final int SHA1_OFFSET = 1; | ||
private final int TOTP_OFFSET = 0; | ||
|
||
public AddTokenDialog(Context ctx) { | ||
super(ctx); | ||
|
||
setTitle(R.string.add_token); | ||
setView(getLayoutInflater().inflate(R.layout.manual, null)); | ||
|
||
setButton(BUTTON_NEGATIVE, ctx.getString(android.R.string.cancel), new OnClickListener() { | ||
@Override | ||
public void onClick(DialogInterface dialog, int which) { | ||
} | ||
}); | ||
|
||
setButton(BUTTON_POSITIVE, ctx.getString(R.string.add), new OnClickListener() { | ||
@Override | ||
public void onClick(DialogInterface dialog, int which) { | ||
// Get the fields | ||
String issuer = Uri.encode(((EditText) findViewById(R.id.issuer)).getText().toString()); | ||
String id = Uri.encode(((EditText) findViewById(R.id.id)).getText().toString()); | ||
String secret = Uri.encode(((EditText) findViewById(R.id.secret)).getText().toString()); | ||
String type = ((Spinner) findViewById(R.id.type)).getSelectedItemId() == TOTP_OFFSET ? "totp" : "hotp"; | ||
String algorithm = ((Spinner) findViewById(R.id.algorithm)).getSelectedItem().toString().toLowerCase(Locale.US); | ||
int interval = Integer.parseInt(((EditText) findViewById(R.id.interval)).getText().toString()); | ||
int digits = ((RadioButton) findViewById(R.id.digits6)).isChecked() ? 6 : 8; | ||
|
||
// Create the URI | ||
String uri = String.format(Locale.US, "otpauth://%s/%s:%s?secret=%s&algorithm=%s&digits=%d", | ||
type, issuer, id, secret, algorithm, digits); | ||
if (type.equals("totp")) | ||
uri = uri.concat(String.format("&period=%d", interval)); | ||
else | ||
uri = uri.concat(String.format("&counter=%d", interval)); | ||
|
||
// Add the token | ||
addToken(uri); | ||
} | ||
}); | ||
} | ||
|
||
@Override | ||
public void onAttachedToWindow() { | ||
super.onAttachedToWindow(); | ||
|
||
// Disable the Add button | ||
getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); | ||
|
||
// Set constraints on when the Add button is enabled | ||
((EditText) findViewById(R.id.issuer)).addTextChangedListener(new AddTokenTextWatcher(this)); | ||
((EditText) findViewById(R.id.id)).addTextChangedListener(new AddTokenTextWatcher(this)); | ||
((EditText) findViewById(R.id.secret)).addTextChangedListener(new AddTokenSecretTextWatcher(this)); | ||
((EditText) findViewById(R.id.interval)).addTextChangedListener(new AddTokenTextWatcher(this)); | ||
|
||
// Select the default algorithm | ||
((Spinner) findViewById(R.id.algorithm)).setSelection(SHA1_OFFSET); | ||
|
||
// Setup the Interval / Counter toggle | ||
((Spinner) findViewById(R.id.type)).setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { | ||
@Override | ||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { | ||
if (position == 0) { | ||
((TextView) findViewById(R.id.interval_label)).setText(R.string.interval); | ||
((EditText) findViewById(R.id.interval)).setText("30"); | ||
} else { | ||
((TextView) findViewById(R.id.interval_label)).setText(R.string.counter); | ||
((EditText) findViewById(R.id.interval)).setText("0"); | ||
} | ||
} | ||
|
||
@Override | ||
public void onNothingSelected(AdapterView<?> parent) { | ||
|
||
} | ||
}); | ||
|
||
} | ||
|
||
public abstract void addToken(String uri); | ||
} |
27 changes: 27 additions & 0 deletions
27
src/org/fedorahosted/freeotp/AddTokenSecretTextWatcher.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package org.fedorahosted.freeotp; | ||
|
||
import android.app.AlertDialog; | ||
import android.text.Editable; | ||
|
||
public class AddTokenSecretTextWatcher extends AddTokenTextWatcher { | ||
public AddTokenSecretTextWatcher(AlertDialog dialog) { | ||
super(dialog); | ||
} | ||
|
||
@Override | ||
public void afterTextChanged(Editable s) { | ||
super.afterTextChanged(s); | ||
|
||
if (s.length() == 0) | ||
return; | ||
|
||
boolean haveData = false; | ||
for (int i = s.length() - 1; i >= 0; i--) { | ||
char c = s.charAt(i); | ||
if (c != '=') | ||
haveData = true; | ||
else if (haveData) | ||
s.delete(i, i + 1); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package org.fedorahosted.freeotp; | ||
|
||
import android.app.AlertDialog; | ||
import android.text.Editable; | ||
import android.text.TextWatcher; | ||
import android.widget.Button; | ||
import android.widget.EditText; | ||
|
||
public class AddTokenTextWatcher implements TextWatcher { | ||
private final AlertDialog dialog; | ||
|
||
public AddTokenTextWatcher(AlertDialog dialog) { | ||
this.dialog = dialog; | ||
} | ||
|
||
@Override | ||
public void beforeTextChanged(CharSequence s, int start, int count, int after) { | ||
|
||
} | ||
|
||
@Override | ||
public void onTextChanged(CharSequence s, int start, int before, int count) { | ||
Button b = dialog.getButton(AlertDialog.BUTTON_POSITIVE); | ||
|
||
b.setEnabled(false); | ||
|
||
if (((EditText) dialog.findViewById(R.id.issuer)).getText().length() == 0) | ||
return; | ||
|
||
if (((EditText) dialog.findViewById(R.id.id)).getText().length() == 0) | ||
return; | ||
|
||
if (((EditText) dialog.findViewById(R.id.secret)).getText().length() == 0 || | ||
((EditText) dialog.findViewById(R.id.secret)).getText().length() % 8 != 0) | ||
return; | ||
|
||
if (((EditText) dialog.findViewById(R.id.interval)).getText().length() == 0) | ||
return; | ||
|
||
b.setEnabled(true); | ||
} | ||
|
||
@Override | ||
public void afterTextChanged(Editable s) { | ||
|
||
} | ||
} |
Oops, something went wrong.