<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.fedorahosted.freeotp"
- android:versionCode="4"
- android:versionName="1.2" >
+ android:versionCode="5"
+ android:versionName="1.3" >
<uses-sdk
android:minSdkVersion="11"
android:text="@string/id" />
<EditText
- android:id="@+id/id"
+ android:id="@+id/label"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
+++ /dev/null
-/*
- * FreeOTP
- *
- * Authors: Nathaniel McCallum <npmccallum@redhat.com>
- *
- * Copyright (C) 2013 Nathaniel McCallum, Red Hat
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-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);
-}
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == AlertDialog.BUTTON_NEUTRAL) {
- new AddTokenDialog(getActivity()) {
- @Override
- public void addToken(String uri) {
- ((MainActivity) getActivity()).tokenURIReceived(uri);
- }
- }.show();
+ new ManualDialogFragment().show(getFragmentManager(),
+ ManualDialogFragment.FRAGMENT_TAG);
}
}
new CameraDialogFragment().show(getFragmentManager(),
CameraDialogFragment.FRAGMENT_TAG);
} else {
- new AddTokenDialog(this) {
- @Override
- public void addToken(String uri) {
- tokenURIReceived(uri);
- }
- }.show();
+ new ManualDialogFragment().show(getFragmentManager(),
+ ManualDialogFragment.FRAGMENT_TAG);
}
return true;
--- /dev/null
+/*
+ * FreeOTP
+ *
+ * Authors: Nathaniel McCallum <npmccallum@redhat.com>
+ *
+ * Copyright (C) 2013 Nathaniel McCallum, Red Hat
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.fedorahosted.freeotp;
+
+import java.util.Locale;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.net.Uri;
+import android.text.TextWatcher;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.EditText;
+import android.widget.RadioButton;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+public class ManualDialogFragment extends BaseAlertDialogFragment implements OnItemSelectedListener {
+ public static final String FRAGMENT_TAG = "fragment_camera";
+ private static final String DEFAULT_INTERVAL = "30";
+ private static final String DEFAULT_COUNTER = "0";
+
+ private final int SHA1_OFFSET = 1;
+ private final int TOTP_OFFSET = 0;
+ private EditText mIssuer;
+ private EditText mLabel;
+ private EditText mSecret;
+ private EditText mInterval;
+ private Spinner mAlgorithm;
+ private Spinner mType;
+
+ public ManualDialogFragment() {
+ super(R.string.add_token, R.layout.manual, android.R.string.cancel, 0, R.string.add);
+ }
+
+ @Override
+ protected void onViewInflated(View view) {
+ mIssuer = (EditText) view.findViewById(R.id.issuer);
+ mLabel = (EditText) view.findViewById(R.id.label);
+ mSecret = (EditText) view.findViewById(R.id.secret);
+ mInterval = (EditText) view.findViewById(R.id.interval);
+ mAlgorithm = (Spinner) view.findViewById(R.id.algorithm);
+ mType = (Spinner) view.findViewById(R.id.type);
+
+ // Select the default algorithm
+ mAlgorithm.setSelection(SHA1_OFFSET);
+
+ // Setup the Interval / Counter toggle
+ mType.setOnItemSelectedListener(this);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ AlertDialog ad = (AlertDialog) getDialog();
+
+ // Disable the Add button
+ ad.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
+
+ // Set constraints on when the Add button is enabled
+ TextWatcher tw = new ManualTextWatcher(ad);
+ mIssuer.addTextChangedListener(tw);
+ mLabel.addTextChangedListener(tw);
+ mSecret.addTextChangedListener(new ManualSecretTextWatcher(ad));
+ mInterval.addTextChangedListener(tw);
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which != AlertDialog.BUTTON_POSITIVE)
+ return;
+
+ // Get the fields
+ String issuer = Uri.encode(mIssuer.getText().toString());
+ String label = Uri.encode(mLabel.getText().toString());
+ String secret = Uri.encode(mSecret.getText().toString());
+ String type = mType.getSelectedItemId() == TOTP_OFFSET ? "totp" : "hotp";
+ String algorithm = mAlgorithm.getSelectedItem().toString().toLowerCase(Locale.US);
+ int interval = Integer.parseInt(mInterval.getText().toString());
+ int digits = ((RadioButton) getDialog().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, label, 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
+ if (uri != null)
+ ((MainActivity) getActivity()).tokenURIReceived(uri);
+ }
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ TextView tv = (TextView) getDialog().findViewById(R.id.interval_label);
+ if (position == 0) {
+ tv.setText(R.string.interval);
+ mInterval.setText(DEFAULT_INTERVAL);
+ } else {
+ tv.setText(R.string.counter);
+ mInterval.setText(DEFAULT_COUNTER);
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+}
import android.app.AlertDialog;
import android.text.Editable;
-public class AddTokenSecretTextWatcher extends AddTokenTextWatcher {
- public AddTokenSecretTextWatcher(AlertDialog dialog) {
+public class ManualSecretTextWatcher extends ManualTextWatcher {
+ public ManualSecretTextWatcher(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);
+ if (s.length() != 0) {
+ // Ensure that = is only permitted at the end
+ 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);
+ }
}
+
+ super.afterTextChanged(s);
}
}
import android.widget.Button;
import android.widget.EditText;
-public class AddTokenTextWatcher implements TextWatcher {
- private final AlertDialog dialog;
+public class ManualTextWatcher implements TextWatcher {
+ private final Button mButton;
+ private final EditText mIssuer;
+ private final EditText mLabel;
+ private final EditText mSecret;
+ private final EditText mInterval;
- public AddTokenTextWatcher(AlertDialog dialog) {
- this.dialog = dialog;
+ public ManualTextWatcher(AlertDialog dialog) {
+ mButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
+ mIssuer = (EditText) dialog.findViewById(R.id.issuer);
+ mLabel = (EditText) dialog.findViewById(R.id.label);
+ mSecret = (EditText) dialog.findViewById(R.id.secret);
+ mInterval = (EditText) dialog.findViewById(R.id.interval);
}
@Override
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
- Button b = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
+ mButton.setEnabled(false);
- b.setEnabled(false);
-
- if (((EditText) dialog.findViewById(R.id.issuer)).getText().length() == 0)
+ if (mIssuer.getText().length() == 0)
return;
- if (((EditText) dialog.findViewById(R.id.id)).getText().length() == 0)
+ if (mLabel.getText().length() == 0)
return;
- if (((EditText) dialog.findViewById(R.id.secret)).getText().length() == 0 ||
- ((EditText) dialog.findViewById(R.id.secret)).getText().length() % 8 != 0)
+ if (mSecret.getText().length() == 0 ||
+ mSecret.getText().length() % 8 != 0)
return;
- if (((EditText) dialog.findViewById(R.id.interval)).getText().length() == 0)
+ if (mInterval.getText().length() == 0)
return;
- b.setEnabled(true);
+ mButton.setEnabled(true);
}
@Override