diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle index cd8142174..f4d18e82d 100644 --- a/OpenKeychain/build.gradle +++ b/OpenKeychain/build.gradle @@ -50,7 +50,6 @@ dependencies { compile 'org.commonjava.googlecode.markdown4j:markdown4j:2.2-cj-1.0' compile 'org.ocpsoft.prettytime:prettytime:4.0.1.Final' compile 'com.splitwise:tokenautocomplete:2.0.7@aar' - compile 'com.github.pinball83:masked-edittext:1.0.3' compile 'se.emilsjolander:stickylistheaders:2.7.0' compile 'org.sufficientlysecure:html-textview:1.3' compile 'org.sufficientlysecure:donations:2.4' @@ -101,7 +100,6 @@ dependencyVerification { 'org.commonjava.googlecode.markdown4j:markdown4j:e952e825d29e1317d96f79f346bfb6786c7c5eef50bd26e54a80823704b62e13', 'org.ocpsoft.prettytime:prettytime:ef7098d973ae78b57d1a22dc37d3b8a771bf030301300e24055d676b6cdc5e75', 'com.splitwise:tokenautocomplete:f56239588390f103b270b7c12361d99b06313a5a0410dc7f66e241ac4baf9baa', - 'com.github.pinball83:masked-edittext:b1913d86482c7066ebb7831696773ac131865dc441cf8a3fc41d3b7d5691724e', 'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb', 'org.sufficientlysecure:donations:96f8197bab26dfe41900d824f10f8f1914519cd62eedb77bdac5b223eccdf0a6', 'org.sufficientlysecure:html-textview:39048e35894e582adada388e6c00631803283f8defed8e07ad58a5f284f272ee', diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeFragment.java index e7ff6ce46..65c51969e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeFragment.java @@ -18,14 +18,6 @@ package org.sufficientlysecure.keychain.ui; -import java.io.File; -import java.io.IOException; -import java.security.SecureRandom; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import java.util.Random; - import android.animation.ArgbEvaluator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; @@ -41,9 +33,8 @@ import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager.OnBackStackChangedListener; import android.text.Editable; -import android.text.InputType; +import android.text.TextUtils; import android.text.TextWatcher; -import android.view.ActionMode; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -52,12 +43,9 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.animation.AccelerateInterpolator; -import android.view.inputmethod.EditorInfo; import android.widget.EditText; import android.widget.TextView; -import com.github.pinball83.maskededittext.MaskedEditText; - import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.ExportResult; @@ -71,6 +59,14 @@ import org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator; import org.sufficientlysecure.keychain.util.FileHelper; import org.sufficientlysecure.keychain.util.Passphrase; +import java.io.File; +import java.io.IOException; +import java.security.SecureRandom; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.Random; + public class BackupCodeFragment extends CryptoOperationFragment implements OnBackStackChangedListener { @@ -96,7 +92,7 @@ public class BackupCodeFragment extends CryptoOperationFragment=16 + textView.setBackgroundDrawable(null); + } + } + + setupEditTextFocusNext(mCodeEditText); setupEditTextSuccessListener(mCodeEditText); - // prevent selection action mode, partially circumventing text selection bug - mCodeEditText.setCustomSelectionActionModeCallback(new ActionMode.Callback() { - @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - return false; - } - - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - return false; - } - - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - return false; - } - - @Override - public void onDestroyActionMode(ActionMode mode) { - - } - }); - - - TextView codeDisplayText = (TextView) view.findViewById(R.id.backup_code_display); - setupAutomaticLinebreak(codeDisplayText); - - // set background to null in TextViews - this will retain padding from EditText style! - // noinspection deprecation, setBackground(Drawable) is API level >=16 - codeDisplayText.setBackgroundDrawable(null); - - codeDisplayText.setText(mBackupCode); - mStatusAnimator = (ToolableViewAnimator) view.findViewById(R.id.status_animator); mTitleAnimator = (ToolableViewAnimator) view.findViewById(R.id.title_animator); mCodeFieldsAnimator = (ToolableViewAnimator) view.findViewById(R.id.code_animator); @@ -347,67 +346,76 @@ public class BackupCodeFragment extends CryptoOperationFragment - * NOTE: I was not able to get this behaviour using XML! - * Looks like the order of these method calls matter, see http://stackoverflow.com/a/11171307 - */ - private void setupAutomaticLinebreak(TextView textview) { - textview.setSingleLine(true); - textview.setMaxLines(6); - textview.setHorizontallyScrolling(false); - } + private void setupEditTextSuccessListener(final EditText[] backupCodes) { + for (EditText backupCode : backupCodes) { - private void setupEditTextSuccessListener(final MaskedEditText backupCode) { - backupCode.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { + backupCode.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - @Override - public void afterTextChanged(Editable s) { - String currentBackupCode = backupCode.getText().toString(); - boolean inInputState = mCurrentState == BackupCodeState.STATE_INPUT - || mCurrentState == BackupCodeState.STATE_INPUT_ERROR; - boolean partIsComplete = (currentBackupCode.indexOf(' ') == -1); - if (!inInputState || !partIsComplete) { - return; } - checkIfCodeIsCorrect(currentBackupCode); - } - }); + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void afterTextChanged(Editable s) { + if (s.length() > 4) { + throw new AssertionError("max length of each field is 4!"); + } + + boolean inInputState = mCurrentState == BackupCodeState.STATE_INPUT + || mCurrentState == BackupCodeState.STATE_INPUT_ERROR; + boolean partIsComplete = s.length() == 4; + if (!inInputState || !partIsComplete) { + return; + } + + checkIfCodeIsCorrect(); + } + }); + + } } - private void checkIfCodeIsCorrect(String currentBackupCode) { + private void checkIfCodeIsCorrect() { if (Constants.DEBUG && mDebugModeAcceptAnyCode) { switchState(BackupCodeState.STATE_OK, true); return; } - if (currentBackupCode.equals(mBackupCode)) { + StringBuilder backupCodeInput = new StringBuilder(26); + for (EditText editText : mCodeEditText) { + if (editText.getText().length() < 4) { + return; + } + backupCodeInput.append(editText.getText()); + backupCodeInput.append('-'); + } + backupCodeInput.deleteCharAt(backupCodeInput.length() - 1); + + // if they don't match, do nothing + if (backupCodeInput.toString().equals(mBackupCode)) { switchState(BackupCodeState.STATE_OK, true); return; } switchState(BackupCodeState.STATE_INPUT_ERROR, true); + } private static void animateFlashText( - final TextView textView, int color1, int color2, boolean staySecondColor) { + final TextView[] textViews, int color1, int color2, boolean staySecondColor) { ValueAnimator anim = ValueAnimator.ofObject(new ArgbEvaluator(), color1, color2); anim.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animator) { - textView.setTextColor((Integer) animator.getAnimatedValue()); + for (TextView textView : textViews) { + textView.setTextColor((Integer) animator.getAnimatedValue()); + } } }); anim.setRepeatMode(ValueAnimator.REVERSE); @@ -418,6 +426,34 @@ public class BackupCodeFragment extends CryptoOperationFragment - * NOTE: I was not able to get this behaviour using XML! - * Looks like the order of these method calls matter, see http://stackoverflow.com/a/11171307 - */ - private void setupAutomaticLinebreak(TextView textview) { - textview.setSingleLine(true); - textview.setMaxLines(6); - textview.setHorizontallyScrolling(false); + private static void setupEditTextFocusNext(final EditText[] backupCodes) { + for (int i = 0; i < backupCodes.length - 1; i++) { + + final int next = i + 1; + + backupCodes[i].addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + boolean inserting = before < count; + boolean cursorAtEnd = (start + count) == 4; + + if (inserting && cursorAtEnd) { + backupCodes[next].requestFocus(); + } + } + + @Override + public void afterTextChanged(Editable s) { + } + }); + + } } + @Override public void onStart() { super.onStart(); @@ -356,8 +370,17 @@ public class PassphraseDialogActivity extends FragmentActivity { public void onClick(View v) { if (mRequiredInput.mType == RequiredInputType.BACKUP_CODE) { - Passphrase passphrase = - new Passphrase(mBackupCodeEditText.getText().toString()); + StringBuilder backupCodeInput = new StringBuilder(26); + for (EditText editText : mBackupCodeEditText) { + if (editText.getText().length() < 4) { + return; + } + backupCodeInput.append(editText.getText()); + backupCodeInput.append('-'); + } + backupCodeInput.deleteCharAt(backupCodeInput.length() - 1); + + Passphrase passphrase = new Passphrase(backupCodeInput.toString()); finishCaching(passphrase); return; diff --git a/OpenKeychain/src/main/res/layout-w400dp/backup_code_fragment.xml b/OpenKeychain/src/main/res/layout-w400dp/backup_code_fragment.xml new file mode 100644 index 000000000..407fa2d9e --- /dev/null +++ b/OpenKeychain/src/main/res/layout-w400dp/backup_code_fragment.xml @@ -0,0 +1,459 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +