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
+