From 7c623216cb4072ead174d48f1bd5ce55e78433e4 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Wed, 4 Dec 2013 16:36:43 -0500 Subject: [PATCH] Migrate Manual token entry to use a DialogFragment --- AndroidManifest.xml | 4 +- res/layout/manual.xml | 2 +- .../fedorahosted/freeotp/AddTokenDialog.java | 116 ---------------- .../freeotp/CameraDialogFragment.java | 8 +- .../fedorahosted/freeotp/MainActivity.java | 8 +- .../freeotp/ManualDialogFragment.java | 130 ++++++++++++++++++ ...cher.java => ManualSecretTextWatcher.java} | 28 ++-- ...extWatcher.java => ManualTextWatcher.java} | 32 +++-- 8 files changed, 170 insertions(+), 158 deletions(-) delete mode 100644 src/org/fedorahosted/freeotp/AddTokenDialog.java create mode 100644 src/org/fedorahosted/freeotp/ManualDialogFragment.java rename src/org/fedorahosted/freeotp/{AddTokenSecretTextWatcher.java => ManualSecretTextWatcher.java} (70%) rename src/org/fedorahosted/freeotp/{AddTokenTextWatcher.java => ManualTextWatcher.java} (59%) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 616e0ae..e7818cb 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -21,8 +21,8 @@ + android:versionCode="5" + android:versionName="1.3" > - * - * 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); -} diff --git a/src/org/fedorahosted/freeotp/CameraDialogFragment.java b/src/org/fedorahosted/freeotp/CameraDialogFragment.java index 5fe6448..f4c7760 100644 --- a/src/org/fedorahosted/freeotp/CameraDialogFragment.java +++ b/src/org/fedorahosted/freeotp/CameraDialogFragment.java @@ -52,12 +52,8 @@ public class CameraDialogFragment extends BaseAlertDialogFragment implements Sur @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); } } diff --git a/src/org/fedorahosted/freeotp/MainActivity.java b/src/org/fedorahosted/freeotp/MainActivity.java index 56737ff..1eb632a 100644 --- a/src/org/fedorahosted/freeotp/MainActivity.java +++ b/src/org/fedorahosted/freeotp/MainActivity.java @@ -101,12 +101,8 @@ public class MainActivity extends Activity implements OnMenuItemClickListener { 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; diff --git a/src/org/fedorahosted/freeotp/ManualDialogFragment.java b/src/org/fedorahosted/freeotp/ManualDialogFragment.java new file mode 100644 index 0000000..9d525df --- /dev/null +++ b/src/org/fedorahosted/freeotp/ManualDialogFragment.java @@ -0,0 +1,130 @@ +/* + * FreeOTP + * + * Authors: Nathaniel McCallum + * + * 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) { + } +} diff --git a/src/org/fedorahosted/freeotp/AddTokenSecretTextWatcher.java b/src/org/fedorahosted/freeotp/ManualSecretTextWatcher.java similarity index 70% rename from src/org/fedorahosted/freeotp/AddTokenSecretTextWatcher.java rename to src/org/fedorahosted/freeotp/ManualSecretTextWatcher.java index 68752ee..8c77e40 100644 --- a/src/org/fedorahosted/freeotp/AddTokenSecretTextWatcher.java +++ b/src/org/fedorahosted/freeotp/ManualSecretTextWatcher.java @@ -23,25 +23,25 @@ package org.fedorahosted.freeotp; 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); } } diff --git a/src/org/fedorahosted/freeotp/AddTokenTextWatcher.java b/src/org/fedorahosted/freeotp/ManualTextWatcher.java similarity index 59% rename from src/org/fedorahosted/freeotp/AddTokenTextWatcher.java rename to src/org/fedorahosted/freeotp/ManualTextWatcher.java index 1f3a4ad..774460e 100644 --- a/src/org/fedorahosted/freeotp/AddTokenTextWatcher.java +++ b/src/org/fedorahosted/freeotp/ManualTextWatcher.java @@ -26,11 +26,19 @@ import android.text.TextWatcher; 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 @@ -40,24 +48,22 @@ public class AddTokenTextWatcher implements TextWatcher { @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 -- 2.43.2