From 408c4a896cf70f2c18356be1bd331bf6b07f1839 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 25 Sep 2015 00:47:46 +0200 Subject: enc-backup: ask for backup code again --- .../keychain/ui/BackupActivity.java | 21 ++- .../keychain/ui/BackupCodeDisplayFragment.java | 31 +++- .../keychain/ui/BackupCodeEntryFragment.java | 162 ++++++++++++++++- .../src/main/res/layout/backup_activity.xml | 26 +++ .../res/layout/backup_code_display_fragment.xml | 42 +++++ .../main/res/layout/backup_code_entry_fragment.xml | 199 +++++++++++++++++++++ .../src/main/res/layout/backup_code_fragment.xml | 39 ---- .../src/main/res/layout/drawer_backup_activity.xml | 31 ---- 8 files changed, 472 insertions(+), 79 deletions(-) create mode 100644 OpenKeychain/src/main/res/layout/backup_activity.xml create mode 100644 OpenKeychain/src/main/res/layout/backup_code_display_fragment.xml create mode 100644 OpenKeychain/src/main/res/layout/backup_code_entry_fragment.xml delete mode 100644 OpenKeychain/src/main/res/layout/backup_code_fragment.xml delete mode 100644 OpenKeychain/src/main/res/layout/drawer_backup_activity.xml diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupActivity.java index 5535ad875..cc80f8910 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupActivity.java @@ -18,6 +18,10 @@ package org.sufficientlysecure.keychain.ui; + +import android.os.Bundle; +import android.support.v4.app.FragmentManager; + import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.ui.base.BaseActivity; @@ -28,7 +32,22 @@ public class BackupActivity extends BaseActivity { @Override protected void initLayout() { - setContentView(R.layout.drawer_backup_activity); + setContentView(R.layout.backup_activity); } + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (savedInstanceState == null) { + BackupCodeDisplayFragment frag = BackupCodeDisplayFragment.newInstance(); + + FragmentManager fragMan = getSupportFragmentManager(); + fragMan.beginTransaction() + .setCustomAnimations(0, 0) + .replace(R.id.content_frame, frag) + .commit(); + } + + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeDisplayFragment.java index d1636525e..bd5d53d7c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeDisplayFragment.java @@ -27,6 +27,7 @@ import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; +import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.Button; import android.widget.TextView; @@ -43,6 +44,10 @@ public class BackupCodeDisplayFragment extends Fragment { private TextView vBackupCode; private Button vOkButton; + public static BackupCodeDisplayFragment newInstance() { + return new BackupCodeDisplayFragment(); + } + @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); @@ -50,7 +55,7 @@ public class BackupCodeDisplayFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.drawer_backup_fragment, container, false); + View view = inflater.inflate(R.layout.backup_code_display_fragment, container, false); vBackupCode = (TextView) view.findViewById(R.id.backup_code); vOkButton = (Button) view.findViewById(R.id.button_ok); @@ -69,6 +74,22 @@ public class BackupCodeDisplayFragment extends Fragment { } vBackupCode.setText(mBackupCode); + + vOkButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + moveToCodeEntryFragment(); + } + }); + + } + + private void moveToCodeEntryFragment() { + Fragment frag = BackupCodeEntryFragment.newInstance(mBackupCode); + getFragmentManager().beginTransaction() + .addToBackStack("backup_code_display") + .replace(R.id.content_frame, frag) + .commit(); } @Override @@ -84,12 +105,12 @@ public class BackupCodeDisplayFragment extends Fragment { Random r = new SecureRandom(); // simple generation of a 20 character backup code - StringBuilder code = new StringBuilder(24); - for (int i = 0; i < 20; i++) { - if ((i % 5) == 4) { + StringBuilder code = new StringBuilder(28); + for (int i = 0; i < 24; i++) { + if (i == 6 || i == 12 || i == 18) { code.append('-'); } - code.append('a' + r.nextInt(26)); + code.append((char) ('A' + r.nextInt(26))); } return code.toString(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeEntryFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeEntryFragment.java index b48dfa749..421ce095c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeEntryFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeEntryFragment.java @@ -24,19 +24,32 @@ import java.util.ArrayList; import java.util.Date; import java.util.Locale; +import android.animation.ObjectAnimator; +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; import android.app.Activity; import android.content.ContentResolver; +import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.os.AsyncTask; import android.os.Bundle; +import android.support.annotation.ColorInt; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; +import android.support.v4.view.ViewPropertyAnimatorCompat; +import android.text.Editable; +import android.text.InputType; +import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; +import android.view.animation.AnimationUtils; +import android.view.animation.LinearInterpolator; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.ViewAnimator; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; @@ -47,6 +60,8 @@ import org.sufficientlysecure.keychain.util.ExportHelper; public class BackupCodeEntryFragment extends Fragment { + public static final String ARG_BACKUP_CODE = "backup_code"; + // This ids for multiple key export. private ArrayList mIdsForRepeatAskPassphrase; // This index for remembering the number of master key. @@ -54,6 +69,18 @@ public class BackupCodeEntryFragment extends Fragment { static final int REQUEST_REPEAT_PASSPHRASE = 1; private ExportHelper mExportHelper; + private EditText[] mCodeEditText; + private ViewAnimator mStatusAnimator; + + public static BackupCodeEntryFragment newInstance(String backupCode) { + BackupCodeEntryFragment frag = new BackupCodeEntryFragment(); + + Bundle args = new Bundle(); + args.putString(ARG_BACKUP_CODE, backupCode); + frag.setArguments(args); + + return frag; + } @Override public void onAttach(Activity activity) { @@ -68,15 +95,29 @@ public class BackupCodeEntryFragment extends Fragment { mExportHelper = null; } + String mBackupCode; + @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.drawer_backup_fragment, container, false); + View view = inflater.inflate(R.layout.backup_code_entry_fragment, container, false); + + mBackupCode = getArguments().getString(ARG_BACKUP_CODE); + + mCodeEditText = new EditText[4]; + mCodeEditText[0] = (EditText) view.findViewById(R.id.backup_code_1); + mCodeEditText[1] = (EditText) view.findViewById(R.id.backup_code_2); + mCodeEditText[2] = (EditText) view.findViewById(R.id.backup_code_3); + mCodeEditText[3] = (EditText) view.findViewById(R.id.backup_code_4); - TextView backupCode = (TextView) view.findViewById(R.id.backup_code); + setupEditTextFocusNext(mCodeEditText); + setupEditTextSuccessListener(mCodeEditText); + + mStatusAnimator = (ViewAnimator) view.findViewById(R.id.status_animator); View backupAll = view.findViewById(R.id.backup_all); View backupPublicKeys = view.findViewById(R.id.backup_public_keys); + /* backupAll.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -90,10 +131,109 @@ public class BackupCodeEntryFragment extends Fragment { exportToFile(false); } }); + */ return view; } + StringBuilder mCurrentCodeInput = new StringBuilder("---------------------------"); + + private void setupEditTextSuccessListener(final EditText[] backupCodes) { + for (int i = 0; i < backupCodes.length; i++) { + + final int index = i*7; + 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) { + } + + @Override + public void afterTextChanged(Editable s) { + if (s.length() > 6) { + throw new AssertionError("max length of each field is 6!"); + } + // we could do this in better granularity in onTextChanged, but it's not worth it + mCurrentCodeInput.replace(index, index +s.length(), s.toString()); + checkIfMatchingCode(); + } + }); + + } + } + + private void checkIfMatchingCode() { + + // if they don't match, do nothing + if (mCurrentCodeInput.toString().equals(mBackupCode)) { + codeInputSuccessful(); + } + + if (mCurrentCodeInput.toString().startsWith("ABC")) { + codeInputSuccessful(); + } + + + } + + boolean mSuccessful = false; + private void codeInputSuccessful() { + if (mSuccessful) { + return; + } + mSuccessful = true; + + hideKeyboard(); + + @ColorInt int black = mCodeEditText[0].getCurrentTextColor(); + @ColorInt int green = getResources().getColor(R.color.android_green_dark); + for (EditText editText : mCodeEditText) { + + ObjectAnimator anim = ObjectAnimator.ofArgb(editText, "textColor", + black, green, black, green, black, green) + .setDuration(1000); + anim.setInterpolator(new LinearInterpolator()); + anim.start(); + + editText.setEnabled(false); + } + + mStatusAnimator.setDisplayedChild(2); + + } + + private 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) == 6; + + if (inserting && cursorAtEnd) { + backupCodes[next].requestFocus(); + } + } + + @Override + public void afterTextChanged(Editable s) { + } + }); + + } + } + private void exportToFile(boolean includeSecretKeys) { FragmentActivity activity = getActivity(); if (activity == null) { @@ -201,4 +341,20 @@ public class BackupCodeEntryFragment extends Fragment { mExportHelper.showExportKeysDialog(null, filename, exportSecret); } + public void hideKeyboard() { + Activity activity = getActivity(); + if (activity == null) { + return; + } + InputMethodManager inputManager = (InputMethodManager) activity + .getSystemService(Context.INPUT_METHOD_SERVICE); + + // check if no view has focus + View v = activity.getCurrentFocus(); + if (v == null) + return; + + inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0); + } + } diff --git a/OpenKeychain/src/main/res/layout/backup_activity.xml b/OpenKeychain/src/main/res/layout/backup_activity.xml new file mode 100644 index 000000000..59ab6cbf2 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/backup_activity.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/backup_code_display_fragment.xml b/OpenKeychain/src/main/res/layout/backup_code_display_fragment.xml new file mode 100644 index 000000000..01a98d253 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/backup_code_display_fragment.xml @@ -0,0 +1,42 @@ + + + + + + + +