From 44139de212d6a25ec096ab5488a1fc1970946a42 Mon Sep 17 00:00:00 2001 From: Alexander Bakker Date: Sat, 30 Dec 2017 00:26:16 +0100 Subject: [PATCH] Replace the FAB with a FAB menu and allow manually entering OTP details --- app/build.gradle | 1 + .../me/impy/aegis/EditProfileActivity.java | 66 +++++++++++-------- .../main/java/me/impy/aegis/KeyProfile.java | 6 +- .../main/java/me/impy/aegis/MainActivity.java | 65 +++++++++++++----- .../java/me/impy/aegis/crypto/KeyInfo.java | 8 +-- .../java/me/impy/aegis/db/DatabaseEntry.java | 4 ++ .../res/drawable/fab_label_background.xml | 11 ++++ app/src/main/res/drawable/ic_qrcode_scan.xml | 8 +++ app/src/main/res/layout/activity_main.xml | 29 +++++++- app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/styles.xml | 4 ++ 11 files changed, 152 insertions(+), 51 deletions(-) create mode 100644 app/src/main/res/drawable/fab_label_background.xml create mode 100644 app/src/main/res/drawable/ic_qrcode_scan.xml diff --git a/app/build.gradle b/app/build.gradle index 3daa7e82..5fd16f75 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,5 +40,6 @@ dependencies { compile 'com.mattprecious.swirl:swirl:1.0.0' compile 'com.madgag.spongycastle:core:1.56.0.0' compile 'com.github.apl-devs:appintro:v4.2.2' + compile 'com.getbase:floatingactionbutton:1.10.1' testCompile 'junit:junit:4.12' } diff --git a/app/src/main/java/me/impy/aegis/EditProfileActivity.java b/app/src/main/java/me/impy/aegis/EditProfileActivity.java index 3b90330e..f1cb2a84 100644 --- a/app/src/main/java/me/impy/aegis/EditProfileActivity.java +++ b/app/src/main/java/me/impy/aegis/EditProfileActivity.java @@ -40,52 +40,64 @@ public class EditProfileActivity extends AegisActivity { super.onCreate(savedInstanceState); setContentView(R.layout.activity_edit_profile); - _profile = (KeyProfile) getIntent().getSerializableExtra("KeyProfile"); - ActionBar bar = getSupportActionBar(); bar.setHomeAsUpIndicator(R.drawable.ic_close); bar.setDisplayHomeAsUpEnabled(true); + // if the intent doesn't contain a KeyProfile, create a new oneZ + _profile = (KeyProfile) getIntent().getSerializableExtra("KeyProfile"); + if (_profile == null) { + _profile = new KeyProfile(); + setTitle("Add profile"); + } + + _textName = findViewById(R.id.text_name); + _textIssuer = findViewById(R.id.text_issuer); + _textPeriod = findViewById(R.id.text_period); + _textSecret = findViewById(R.id.text_secret); + _spinnerType = findViewById(R.id.spinner_type); + SpinnerHelper.fillSpinner(this, _spinnerType, R.array.otp_types_array); + _spinnerAlgo = findViewById(R.id.spinner_algo); + SpinnerHelper.fillSpinner(this, _spinnerAlgo, R.array.otp_algo_array); + _spinnerDigits = findViewById(R.id.spinner_digits); + SpinnerHelper.fillSpinner(this, _spinnerDigits, R.array.otp_digits_array); + + updateFields(); + + _textName.addTextChangedListener(_textListener); + _textIssuer.addTextChangedListener(_textListener); + _textPeriod.addTextChangedListener(_textListener); + _textSecret.addTextChangedListener(_textListener); + _spinnerType.setOnTouchListener(_selectedListener); + _spinnerType.setOnItemSelectedListener(_selectedListener); + _spinnerAlgo.setOnTouchListener(_selectedListener); + _spinnerAlgo.setOnItemSelectedListener(_selectedListener); + _spinnerDigits.setOnTouchListener(_selectedListener); + _spinnerDigits.setOnItemSelectedListener(_selectedListener); + } + + private void updateFields() { + DatabaseEntry entry = _profile.getEntry(); ImageView imageView = findViewById(R.id.profile_drawable); imageView.setImageDrawable(_profile.getDrawable()); - DatabaseEntry entry = _profile.getEntry(); - _textName = findViewById(R.id.text_name); _textName.setText(entry.getName()); - _textName.addTextChangedListener(watcher); - - _textIssuer = findViewById(R.id.text_issuer); _textIssuer.setText(entry.getInfo().getIssuer()); - _textIssuer.addTextChangedListener(watcher); - - _textPeriod = findViewById(R.id.text_period); _textPeriod.setText(Integer.toString(entry.getInfo().getPeriod())); - _textPeriod.addTextChangedListener(watcher); - _textSecret = findViewById(R.id.text_secret); - _textSecret.setText(Base32.encodeOriginal(entry.getInfo().getSecret())); - _textSecret.addTextChangedListener(watcher); + byte[] secretBytes = entry.getInfo().getSecret(); + if (secretBytes != null) { + _textSecret.setText(Base32.encodeOriginal(secretBytes)); + } String type = entry.getInfo().getType(); - _spinnerType = findViewById(R.id.spinner_type); - SpinnerHelper.fillSpinner(this, _spinnerType, R.array.otp_types_array); _spinnerType.setSelection(getStringResourceIndex(R.array.otp_types_array, type), false); - _spinnerType.setOnTouchListener(_selectedListener); - _spinnerType.setOnItemSelectedListener(_selectedListener); String algo = entry.getInfo().getAlgorithm(false); - _spinnerAlgo = findViewById(R.id.spinner_algo); - SpinnerHelper.fillSpinner(this, _spinnerAlgo, R.array.otp_algo_array); _spinnerAlgo.setSelection(getStringResourceIndex(R.array.otp_algo_array, algo), false); - _spinnerAlgo.setOnTouchListener(_selectedListener); - _spinnerAlgo.setOnItemSelectedListener(_selectedListener); String digits = Integer.toString(entry.getInfo().getDigits()); - _spinnerDigits = findViewById(R.id.spinner_digits); - SpinnerHelper.fillSpinner(this, _spinnerDigits, R.array.otp_digits_array); _spinnerDigits.setSelection(getStringResourceIndex(R.array.otp_digits_array, digits), false); - _spinnerDigits.setOnTouchListener(_selectedListener); - _spinnerDigits.setOnItemSelectedListener(_selectedListener); } @Override @@ -185,7 +197,7 @@ public class EditProfileActivity extends AegisActivity { _edited = true; } - private TextWatcher watcher = new TextWatcher() { + private TextWatcher _textListener = new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { onFieldEdited(); diff --git a/app/src/main/java/me/impy/aegis/KeyProfile.java b/app/src/main/java/me/impy/aegis/KeyProfile.java index bf472dc6..2454072d 100644 --- a/app/src/main/java/me/impy/aegis/KeyProfile.java +++ b/app/src/main/java/me/impy/aegis/KeyProfile.java @@ -13,6 +13,10 @@ public class KeyProfile implements Serializable { private String _code; private DatabaseEntry _entry; + public KeyProfile() { + this(new DatabaseEntry()); + } + public KeyProfile(DatabaseEntry entry) { _entry = entry; } @@ -35,7 +39,7 @@ public class KeyProfile implements Serializable { public TextDrawable getDrawable() { String name = _entry.getName(); - if (name == null) { + if (name == null || name.length() <= 1) { return null; } diff --git a/app/src/main/java/me/impy/aegis/MainActivity.java b/app/src/main/java/me/impy/aegis/MainActivity.java index a5c6fdcf..dd1ab8cc 100644 --- a/app/src/main/java/me/impy/aegis/MainActivity.java +++ b/app/src/main/java/me/impy/aegis/MainActivity.java @@ -8,7 +8,6 @@ import android.content.DialogInterface; import android.content.Intent; import android.media.MediaScannerConnection; import android.support.design.widget.BottomSheetDialog; -import android.support.design.widget.FloatingActionButton; import android.support.v7.app.AlertDialog; import android.os.Bundle; import android.support.v7.widget.Toolbar; @@ -18,6 +17,9 @@ import android.view.View; import android.widget.LinearLayout; import android.widget.Toast; +import com.getbase.floatingactionbutton.FloatingActionButton; +import com.getbase.floatingactionbutton.FloatingActionsMenu; + import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.lang.reflect.UndeclaredThrowableException; @@ -32,13 +34,14 @@ import me.impy.aegis.util.ByteInputStream; public class MainActivity extends AegisActivity implements KeyProfileView.Listener { // activity request codes - private static final int CODE_GET_KEYINFO = 0; + private static final int CODE_SCAN_KEYINFO = 0; private static final int CODE_ADD_KEYINFO = 1; private static final int CODE_EDIT_KEYINFO = 2; - private static final int CODE_DO_INTRO = 3; - private static final int CODE_DECRYPT = 4; - private static final int CODE_IMPORT = 5; - private static final int CODE_PREFERENCES = 6; + private static final int CODE_ENTER_KEYINFO = 3; + private static final int CODE_DO_INTRO = 4; + private static final int CODE_DECRYPT = 5; + private static final int CODE_IMPORT = 6; + private static final int CODE_PREFERENCES = 7; // permission request codes private static final int CODE_PERM_EXPORT = 0; @@ -60,8 +63,7 @@ public class MainActivity extends AegisActivity implements KeyProfileView.Listen // set up the main view setContentView(R.layout.activity_main); - Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); + setSupportActionBar(findViewById(R.id.toolbar)); // set up the key profile view _keyProfileView = (KeyProfileView) getSupportFragmentManager().findFragmentById(R.id.key_profiles); @@ -69,9 +71,15 @@ public class MainActivity extends AegisActivity implements KeyProfileView.Listen _keyProfileView.setShowIssuer(_app.getPreferences().getBoolean("pref_issuer", false)); // set up the floating action button - FloatingActionButton fab = findViewById(R.id.fab); - fab.setEnabled(true); - fab.setOnClickListener(view -> onGetKeyInfo()); + FloatingActionsMenu fabMenu = findViewById(R.id.fab); + findViewById(R.id.fab_enter).setOnClickListener(view -> { + fabMenu.collapse(); + onEnterKeyInfo(); + }); + findViewById(R.id.fab_scan).setOnClickListener(view -> { + fabMenu.collapse(); + onScanKeyInfo(); + }); // skip this part if this is the not initial startup and the database has been unlocked if (!_app.isRunning() && _db.isLocked()) { @@ -133,8 +141,8 @@ public class MainActivity extends AegisActivity implements KeyProfileView.Listen } switch (requestCode) { - case CODE_GET_KEYINFO: - onGetKeyInfoResult(resultCode, data); + case CODE_SCAN_KEYINFO: + onScanKeyInfoResult(resultCode, data); break; case CODE_ADD_KEYINFO: onAddKeyInfoResult(resultCode, data); @@ -142,6 +150,9 @@ public class MainActivity extends AegisActivity implements KeyProfileView.Listen case CODE_EDIT_KEYINFO: onEditKeyInfoResult(resultCode, data); break; + case CODE_ENTER_KEYINFO: + onEnterKeyInfoResult(resultCode, data); + break; case CODE_DO_INTRO: onDoIntroResult(resultCode, data); break; @@ -172,7 +183,7 @@ public class MainActivity extends AegisActivity implements KeyProfileView.Listen onImport(); break; case CODE_PERM_CAMERA: - onGetKeyInfo(); + onScanKeyInfo(); break; } } @@ -297,7 +308,12 @@ public class MainActivity extends AegisActivity implements KeyProfileView.Listen saveDatabase(); } - private void onGetKeyInfo() { + private void onEnterKeyInfo() { + Intent intent = new Intent(this, EditProfileActivity.class); + startActivityForResult(intent, CODE_ENTER_KEYINFO); + } + + private void onScanKeyInfo() { if (!PermissionHelper.request(this, CODE_PERM_CAMERA, Manifest.permission.CAMERA)) { return; } @@ -305,7 +321,7 @@ public class MainActivity extends AegisActivity implements KeyProfileView.Listen startScanActivity(); } - private void onGetKeyInfoResult(int resultCode, Intent data) { + private void onScanKeyInfoResult(int resultCode, Intent data) { if (resultCode == RESULT_OK) { KeyProfile keyProfile = (KeyProfile)data.getSerializableExtra("KeyProfile"); Intent intent = new Intent(this, AddProfileActivity.class); @@ -339,6 +355,21 @@ public class MainActivity extends AegisActivity implements KeyProfileView.Listen } } + private void onEnterKeyInfoResult(int resultCode, Intent data) { + if (resultCode == RESULT_OK) { + KeyProfile profile = (KeyProfile) data.getSerializableExtra("KeyProfile"); + try { + addKey(profile); + } catch (Exception e) { + e.printStackTrace(); + Toast.makeText(this, "An error occurred while trying to add an entry", Toast.LENGTH_SHORT).show(); + return; + } + _keyProfileView.addKey(profile); + saveDatabase(); + } + } + private void addKey(KeyProfile profile) { profile.refreshCode(); @@ -395,7 +426,7 @@ public class MainActivity extends AegisActivity implements KeyProfileView.Listen private void startScanActivity() { Intent scannerActivity = new Intent(getApplicationContext(), ScannerActivity.class); - startActivityForResult(scannerActivity, CODE_GET_KEYINFO); + startActivityForResult(scannerActivity, CODE_SCAN_KEYINFO); } private boolean doShortcutActions() { diff --git a/app/src/main/java/me/impy/aegis/crypto/KeyInfo.java b/app/src/main/java/me/impy/aegis/crypto/KeyInfo.java index 7bd653fa..14720ff0 100644 --- a/app/src/main/java/me/impy/aegis/crypto/KeyInfo.java +++ b/app/src/main/java/me/impy/aegis/crypto/KeyInfo.java @@ -7,11 +7,11 @@ import java.io.Serializable; import me.impy.aegis.encoding.Base32; public class KeyInfo implements Serializable { - private String _type; + private String _type = "totp"; private byte[] _secret; - private String _accountName; - private String _issuer; - private long _counter; + private String _accountName = ""; + private String _issuer = ""; + private long _counter = 0; private String _algorithm = "SHA1"; private int _digits = 6; private int _period = 30; diff --git a/app/src/main/java/me/impy/aegis/db/DatabaseEntry.java b/app/src/main/java/me/impy/aegis/db/DatabaseEntry.java index 54c0480f..c3a4008d 100644 --- a/app/src/main/java/me/impy/aegis/db/DatabaseEntry.java +++ b/app/src/main/java/me/impy/aegis/db/DatabaseEntry.java @@ -13,6 +13,10 @@ public class DatabaseEntry implements Serializable { private String _icon = ""; private KeyInfo _info; + public DatabaseEntry() { + this(new KeyInfo()); + } + public DatabaseEntry(KeyInfo info) { _info = info; } diff --git a/app/src/main/res/drawable/fab_label_background.xml b/app/src/main/res/drawable/fab_label_background.xml new file mode 100644 index 00000000..908cf84a --- /dev/null +++ b/app/src/main/res/drawable/fab_label_background.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_qrcode_scan.xml b/app/src/main/res/drawable/ic_qrcode_scan.xml new file mode 100644 index 00000000..ee1670e0 --- /dev/null +++ b/app/src/main/res/drawable/ic_qrcode_scan.xml @@ -0,0 +1,8 @@ + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index aa94f93f..a11b3ef6 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -29,13 +29,38 @@ app:layout_behavior="@string/appbar_scrolling_view_behavior"/> - + app:fab_addButtonColorNormal="@color/colorAccent" + app:fab_addButtonColorPressed="@color/colorAccent" + app:fab_labelStyle="@style/fab_label_style" + app:fab_labelsPosition="left"> + + + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 2e903ae0..cace4f78 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -5,6 +5,7 @@ #12b600 #B3E5FC #FF5252 + #FF5252 #212121 #434343 #8e8e8e diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 769f710a..aeff5f45 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -94,5 +94,9 @@ true +