mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-04-21 06:19:12 +00:00
Start hooking up the intro to the database code
Fingerprint stuff has been left out for now
This commit is contained in:
parent
f1b499f101
commit
53e86db187
11 changed files with 311 additions and 633 deletions
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
|
@ -37,7 +37,7 @@
|
|||
<ConfirmationsSetting value="0" id="Add" />
|
||||
<ConfirmationsSetting value="0" id="Remove" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="JDK" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
|
|
|
@ -1,30 +1,35 @@
|
|||
package me.impy.aegis;
|
||||
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.EditText;
|
||||
|
||||
import com.mattprecious.swirl.SwirlView;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import agency.tango.materialintroscreen.SlideFragment;
|
||||
import me.impy.aegis.finger.FingerprintAuthenticationDialogFragment;
|
||||
import me.impy.aegis.crypto.CryptoUtils;
|
||||
import me.impy.aegis.crypto.slots.PasswordSlot;
|
||||
import me.impy.aegis.crypto.slots.Slot;
|
||||
|
||||
public class CustomAuthenticatedSlide extends SlideFragment {
|
||||
private CheckBox checkBox;
|
||||
private RadioButton passwordRadioButton;
|
||||
private EditText textPassword;
|
||||
private EditText textPasswordConfirm;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
final View view = inflater.inflate(R.layout.fragment_authenticated_slide, container, false);
|
||||
View view = inflater.inflate(R.layout.fragment_authenticated_slide, container, false);
|
||||
textPassword = (EditText) view.findViewById(R.id.text_password);
|
||||
textPasswordConfirm = (EditText) view.findViewById(R.id.text_password_confirm);
|
||||
return view;
|
||||
}
|
||||
|
||||
|
@ -40,16 +45,42 @@ public class CustomAuthenticatedSlide extends SlideFragment {
|
|||
|
||||
@Override
|
||||
public boolean canMoveFurther() {
|
||||
return true; //checkBox.isChecked();
|
||||
}
|
||||
|
||||
public void onAuthenticated(FingerprintAuthenticationDialogFragment.Action action, FingerprintManager.CryptoObject obj) {
|
||||
|
||||
char[] password = getEditTextChars(textPassword);
|
||||
char[] passwordConfirm = getEditTextChars(textPasswordConfirm);
|
||||
boolean equal = password.length != 0 && Arrays.equals(password, passwordConfirm);
|
||||
CryptoUtils.zero(password);
|
||||
CryptoUtils.zero(passwordConfirm);
|
||||
return equal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cantMoveFurtherErrorMessage() {
|
||||
return "Ja bijna vriend";
|
||||
//return getString(R.string.error_message);
|
||||
return "Passwords should be equal and non-empty";
|
||||
}
|
||||
|
||||
public Cipher getCipher(PasswordSlot slot, int mode)
|
||||
throws InvalidKeySpecException, NoSuchAlgorithmException,
|
||||
InvalidKeyException, NoSuchPaddingException {
|
||||
char[] password = getPassword(true);
|
||||
byte[] salt = CryptoUtils.generateSalt();
|
||||
SecretKey key = slot.deriveKey(password, salt, CryptoUtils.CRYPTO_ITERATION_COUNT);
|
||||
CryptoUtils.zero(password);
|
||||
|
||||
return Slot.createCipher(key, mode);
|
||||
}
|
||||
|
||||
private char[] getPassword(boolean clear) {
|
||||
char[] password = getEditTextChars(textPassword);
|
||||
if (clear) {
|
||||
textPassword.getText().clear();
|
||||
}
|
||||
return password;
|
||||
}
|
||||
|
||||
private static char[] getEditTextChars(EditText text) {
|
||||
Editable editable = text.getText();
|
||||
char[] chars = new char[editable.length()];
|
||||
editable.getChars(0, editable.length(), chars, 0);
|
||||
return chars;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,53 +1,31 @@
|
|||
package me.impy.aegis;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.app.DialogFragment;
|
||||
import android.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.RadioGroup;
|
||||
|
||||
import com.mattprecious.swirl.SwirlView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import agency.tango.materialintroscreen.SlideFragment;
|
||||
import me.impy.aegis.finger.FingerprintAuthenticationDialogFragment;
|
||||
import me.impy.aegis.finger.SetFingerprintAuthenticationDialog;
|
||||
|
||||
public class CustomAuthenticationSlide extends SlideFragment implements SetFingerprintAuthenticationDialog.InterfaceCommunicator{
|
||||
private CheckBox checkBox;
|
||||
private RadioButton fingerprintRadioButton;
|
||||
private RadioGroup authenticationMethodRadioGroup;
|
||||
|
||||
public static final int DIALOG_FRAGMENT = 1;
|
||||
public class CustomAuthenticationSlide extends SlideFragment {
|
||||
private RadioGroup buttonGroup;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
final View view = inflater.inflate(R.layout.fragment_authentication_slide, container, false);
|
||||
final CustomAuthenticationSlide caller = this;
|
||||
buttonGroup = (RadioGroup) view.findViewById(R.id.rg_authenticationMethod);
|
||||
|
||||
fingerprintRadioButton = (RadioButton) view.findViewById(R.id.rb_fingerprint);
|
||||
fingerprintRadioButton.setOnClickListener(new View.OnClickListener() {
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
SetFingerprintAuthenticationDialog fragment = new SetFingerprintAuthenticationDialog();
|
||||
//fragment.setCryptoObject(new FingerprintManager.CryptoObject(cipher));
|
||||
fragment.setStage(SetFingerprintAuthenticationDialog.Stage.FINGERPRINT);
|
||||
fragment.setCaller(caller);
|
||||
//fragment.setAction(action);
|
||||
fragment.show(getActivity().getFragmentManager(), "dialog");
|
||||
RadioButton button = (RadioButton) view.findViewById(R.id.rb_fingerprint);
|
||||
button.setOnClickListener(v -> {
|
||||
if (canMoveFurther()) {
|
||||
buttonGroup.clearCheck();
|
||||
Toast.makeText(getActivity().getBaseContext(), "Fingerprint is not supported yet", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
|
||||
authenticationMethodRadioGroup = (RadioGroup) view.findViewById(R.id.rg_authenticationMethod);
|
||||
return view;
|
||||
}
|
||||
|
||||
|
@ -63,27 +41,11 @@ public class CustomAuthenticationSlide extends SlideFragment implements SetFinge
|
|||
|
||||
@Override
|
||||
public boolean canMoveFurther() {
|
||||
return authenticationMethodRadioGroup.getCheckedRadioButtonId() != -1;
|
||||
return buttonGroup.getCheckedRadioButtonId() != -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String cantMoveFurtherErrorMessage() {
|
||||
return "Please select an authentication method";
|
||||
//return getString(R.string.error_message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendRequestCode(int code) {
|
||||
if (code == 1) {
|
||||
|
||||
} else if (code == 0){
|
||||
authenticationMethodRadioGroup.clearCheck();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,16 +2,32 @@ package me.impy.aegis;
|
|||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
import agency.tango.materialintroscreen.MaterialIntroActivity;
|
||||
import agency.tango.materialintroscreen.MessageButtonBehaviour;
|
||||
import agency.tango.materialintroscreen.SlideFragmentBuilder;
|
||||
import me.impy.aegis.crypto.CryptResult;
|
||||
import me.impy.aegis.crypto.MasterKey;
|
||||
import me.impy.aegis.crypto.slots.PasswordSlot;
|
||||
import me.impy.aegis.crypto.slots.SlotCollection;
|
||||
import me.impy.aegis.db.Database;
|
||||
import me.impy.aegis.db.DatabaseFile;
|
||||
|
||||
public class IntroActivity extends MaterialIntroActivity {
|
||||
public static final int RESULT_OK = 0;
|
||||
public static final int RESULT_EXCEPTION = 1;
|
||||
|
||||
private CustomAuthenticatedSlide authenticatedSlide;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
|
@ -41,12 +57,64 @@ public class IntroActivity extends MaterialIntroActivity {
|
|||
}, "Permission granted"));
|
||||
|
||||
addSlide(new CustomAuthenticationSlide());
|
||||
addSlide(new CustomAuthenticatedSlide());
|
||||
|
||||
authenticatedSlide = new CustomAuthenticatedSlide();
|
||||
addSlide(authenticatedSlide);
|
||||
|
||||
addSlide(new SlideFragmentBuilder()
|
||||
.backgroundColor(R.color.colorPrimary)
|
||||
.buttonsColor(R.color.colorAccent)
|
||||
.image(R.drawable.intro_shield)
|
||||
.title("All done!")
|
||||
.description("Aegis has been set up and is ready to go.")
|
||||
.build());
|
||||
}
|
||||
|
||||
private void setException(Exception e) {
|
||||
Intent result = new Intent();
|
||||
result.putExtra("exception", e);
|
||||
setResult(RESULT_OK, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
super.onFinish();
|
||||
|
||||
// create the database and database file
|
||||
Database database = new Database();
|
||||
DatabaseFile databaseFile = new DatabaseFile();
|
||||
|
||||
MasterKey masterKey;
|
||||
try {
|
||||
// generate the master key
|
||||
masterKey = MasterKey.generate();
|
||||
|
||||
// encrypt the master key with a key derived from the user's password
|
||||
// and add it to the list of slots
|
||||
SlotCollection slots = databaseFile.getSlots();
|
||||
PasswordSlot slot = new PasswordSlot();
|
||||
Cipher cipher = authenticatedSlide.getCipher(slot, Cipher.ENCRYPT_MODE);
|
||||
masterKey.encryptSlot(slot, cipher);
|
||||
slots.add(slot);
|
||||
|
||||
// finally, save the database
|
||||
byte[] bytes = database.serialize();
|
||||
CryptResult result = masterKey.encrypt(bytes);
|
||||
databaseFile.setContent(result.Data);
|
||||
databaseFile.setCryptParameters(result.Parameters);
|
||||
databaseFile.save(getApplicationContext());
|
||||
} catch (Exception e) {
|
||||
setException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
// send the master key back to the main activity
|
||||
Intent result = new Intent();
|
||||
result.putExtra("key", masterKey);
|
||||
setResult(RESULT_OK, result);
|
||||
|
||||
// skip the intro from now on
|
||||
// TODO: show the intro if we can't find any database files
|
||||
SharedPreferences prefs = this.getSharedPreferences("me.impy.aegis", Context.MODE_PRIVATE);
|
||||
prefs.edit().putBoolean("passedIntro", true).apply();
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import android.content.pm.ShortcutManager;
|
|||
import android.graphics.drawable.Icon;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.design.widget.BottomSheetDialog;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
|
@ -30,7 +29,6 @@ import android.widget.Toast;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -41,30 +39,26 @@ import javax.crypto.Cipher;
|
|||
import javax.crypto.SecretKey;
|
||||
|
||||
import me.impy.aegis.crypto.CryptResult;
|
||||
import me.impy.aegis.crypto.CryptoUtils;
|
||||
import me.impy.aegis.crypto.MasterKey;
|
||||
import me.impy.aegis.crypto.otp.OTP;
|
||||
import me.impy.aegis.crypto.slots.FingerprintSlot;
|
||||
import me.impy.aegis.crypto.slots.PasswordSlot;
|
||||
import me.impy.aegis.crypto.slots.RawSlot;
|
||||
import me.impy.aegis.crypto.slots.Slot;
|
||||
import me.impy.aegis.crypto.slots.SlotCollection;
|
||||
import me.impy.aegis.db.Database;
|
||||
import me.impy.aegis.db.DatabaseFile;
|
||||
import me.impy.aegis.finger.FingerprintAuthenticationDialogFragment;
|
||||
import me.impy.aegis.db.DatabaseManager;
|
||||
import me.impy.aegis.helpers.SimpleItemTouchHelperCallback;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
static final int GET_KEYINFO = 1;
|
||||
static final int ADD_KEYINFO = 2;
|
||||
private static final int CODE_GET_KEYINFO = 0;
|
||||
private static final int CODE_ADD_KEYINFO = 1;
|
||||
private static final int CODE_DO_INTRO = 2;
|
||||
|
||||
RecyclerView rvKeyProfiles;
|
||||
KeyProfileAdapter mKeyProfileAdapter;
|
||||
ArrayList<KeyProfile> mKeyProfiles = new ArrayList<>();
|
||||
MasterKey masterKey;
|
||||
Database database;
|
||||
DatabaseFile databaseFile;
|
||||
private DatabaseManager db;
|
||||
|
||||
boolean nightMode = false;
|
||||
int clickedItemPosition = -1;
|
||||
|
@ -72,11 +66,12 @@ public class MainActivity extends AppCompatActivity {
|
|||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
db = new DatabaseManager(getApplicationContext());
|
||||
|
||||
SharedPreferences prefs = this.getSharedPreferences("me.impy.aegis", Context.MODE_PRIVATE);
|
||||
if (!prefs.getBoolean("passedIntro", false)) {
|
||||
Intent intro = new Intent(this, IntroActivity.class);
|
||||
startActivity(intro);
|
||||
startActivityForResult(intro, CODE_DO_INTRO);
|
||||
}
|
||||
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
@ -95,7 +90,7 @@ public class MainActivity extends AppCompatActivity {
|
|||
fab.setEnabled(true);
|
||||
fab.setOnClickListener(view -> {
|
||||
Intent scannerActivity = new Intent(getApplicationContext(), ScannerActivity.class);
|
||||
startActivityForResult(scannerActivity, GET_KEYINFO);
|
||||
startActivityForResult(scannerActivity, CODE_GET_KEYINFO);
|
||||
});
|
||||
|
||||
rvKeyProfiles = (RecyclerView) findViewById(R.id.rvKeyProfiles);
|
||||
|
@ -123,23 +118,40 @@ public class MainActivity extends AppCompatActivity {
|
|||
}
|
||||
};
|
||||
Collections.sort(mKeyProfiles, comparator);
|
||||
|
||||
loadDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == GET_KEYINFO) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
switch (requestCode) {
|
||||
case CODE_GET_KEYINFO:
|
||||
onGetKeyInfoResult(resultCode, data);
|
||||
break;
|
||||
case CODE_ADD_KEYINFO:
|
||||
onAddKeyInfoResult(resultCode, data);
|
||||
break;
|
||||
case CODE_DO_INTRO:
|
||||
onDoIntroResult(resultCode, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void onGetKeyInfoResult(int resultCode, Intent data) {
|
||||
if (resultCode != RESULT_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
final KeyProfile keyProfile = (KeyProfile)data.getSerializableExtra("KeyProfile");
|
||||
|
||||
Intent intent = new Intent(this, AddProfileActivity.class);
|
||||
intent.putExtra("KeyProfile", keyProfile);
|
||||
startActivityForResult(intent, ADD_KEYINFO);
|
||||
startActivityForResult(intent, CODE_ADD_KEYINFO);
|
||||
}
|
||||
|
||||
private void onAddKeyInfoResult(int resultCode, Intent data) {
|
||||
if (resultCode != RESULT_OK) {
|
||||
return;
|
||||
}
|
||||
else if (requestCode == ADD_KEYINFO) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
|
||||
final KeyProfile keyProfile = (KeyProfile) data.getSerializableExtra("KeyProfile");
|
||||
|
||||
String otp;
|
||||
|
@ -153,7 +165,7 @@ public class MainActivity extends AppCompatActivity {
|
|||
keyProfile.Order = mKeyProfiles.size() + 1;
|
||||
keyProfile.Code = otp;
|
||||
try {
|
||||
database.addKey(keyProfile);
|
||||
db.addKey(keyProfile);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
// TODO: feedback
|
||||
|
@ -164,6 +176,23 @@ public class MainActivity extends AppCompatActivity {
|
|||
mKeyProfileAdapter.notifyDataSetChanged();
|
||||
saveDatabase();
|
||||
}
|
||||
|
||||
private void onDoIntroResult(int resultCode, Intent data) {
|
||||
if (resultCode == IntroActivity.RESULT_EXCEPTION) {
|
||||
// TODO: user feedback
|
||||
Exception e = (Exception) data.getSerializableExtra("exception");
|
||||
throw new UndeclaredThrowableException(e);
|
||||
}
|
||||
|
||||
MasterKey key = (MasterKey) data.getSerializableExtra("key");
|
||||
try {
|
||||
db.load();
|
||||
if (!db.isDecrypted()) {
|
||||
db.setMasterKey(key);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// TODO: feedback
|
||||
throw new UndeclaredThrowableException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,53 +208,16 @@ public class MainActivity extends AppCompatActivity {
|
|||
// update order of keys
|
||||
for (int i = 0; i < mKeyProfiles.size(); i++) {
|
||||
try {
|
||||
database.updateKey(mKeyProfiles.get(i));
|
||||
db.updateKey(mKeyProfiles.get(i));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
saveDatabase();
|
||||
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
private void promptFingerPrint(FingerprintAuthenticationDialogFragment.Action action, Cipher cipher) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
FingerprintAuthenticationDialogFragment fragment = new FingerprintAuthenticationDialogFragment();
|
||||
fragment.setCryptoObject(new FingerprintManager.CryptoObject(cipher));
|
||||
fragment.setStage(FingerprintAuthenticationDialogFragment.Stage.FINGERPRINT);
|
||||
fragment.setAction(action);
|
||||
fragment.show(getFragmentManager(), "");
|
||||
}
|
||||
}
|
||||
|
||||
public void onAuthenticated(FingerprintAuthenticationDialogFragment.Action action, FingerprintManager.CryptoObject obj) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
Cipher cipher = obj.getCipher();
|
||||
switch (action) {
|
||||
case SAVE:
|
||||
saveDatabase();
|
||||
break;
|
||||
case LOAD:
|
||||
loadDatabase();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveDatabase() {
|
||||
try {
|
||||
byte[] bytes = database.serialize();
|
||||
CryptResult result = masterKey.encrypt(bytes);
|
||||
databaseFile.setContent(result.Data);
|
||||
databaseFile.setCryptParameters(result.Parameters);
|
||||
databaseFile.save(getApplicationContext());
|
||||
} catch (Exception e) {
|
||||
throw new UndeclaredThrowableException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private BottomSheetDialog InitializeBottomSheet()
|
||||
{
|
||||
View bottomSheetView = getLayoutInflater ().inflate (R.layout.bottom_sheet_edit_profile, null);
|
||||
|
@ -271,7 +263,7 @@ public class MainActivity extends AppCompatActivity {
|
|||
.setMessage("Are you sure you want to delete this profile?")
|
||||
.setPositiveButton(android.R.string.yes, (dialog, which) -> {
|
||||
try {
|
||||
database.removeKey(profile);
|
||||
db.removeKey(profile);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
//TODO: feedback
|
||||
|
@ -364,37 +356,12 @@ public class MainActivity extends AppCompatActivity {
|
|||
}
|
||||
}
|
||||
|
||||
private void createDatabase() {
|
||||
database = new Database();
|
||||
databaseFile = new DatabaseFile();
|
||||
|
||||
try {
|
||||
masterKey = MasterKey.generate();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// TODO: tell the user to stop using a weird platform
|
||||
throw new UndeclaredThrowableException(e);
|
||||
}
|
||||
|
||||
SlotCollection slots = databaseFile.getSlots();
|
||||
|
||||
try {
|
||||
PasswordSlot slot = new PasswordSlot();
|
||||
byte[] salt = CryptoUtils.generateSalt();
|
||||
SecretKey derivedKey = slot.deriveKey("testpassword".toCharArray(), salt, CryptoUtils.CRYPTO_ITERATION_COUNT);
|
||||
Cipher cipher = Slot.createCipher(derivedKey, Cipher.ENCRYPT_MODE);
|
||||
masterKey.encryptSlot(slot, cipher);
|
||||
slots.add(slot);
|
||||
} catch (Exception e) {
|
||||
throw new UndeclaredThrowableException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadDatabase() {
|
||||
/*private void loadDatabase() {
|
||||
try {
|
||||
databaseFile = DatabaseFile.load(getApplicationContext());
|
||||
} catch (IOException e) {
|
||||
// the database file doesn't exist yet
|
||||
createDatabase();
|
||||
//createDatabase();
|
||||
saveDatabase();
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
|
@ -445,6 +412,15 @@ public class MainActivity extends AppCompatActivity {
|
|||
} catch (Exception e) {
|
||||
throw new UndeclaredThrowableException(e);
|
||||
}
|
||||
}*/
|
||||
|
||||
private void saveDatabase() {
|
||||
try {
|
||||
db.save();
|
||||
} catch (Exception e) {
|
||||
//TODO: feedback
|
||||
throw new UndeclaredThrowableException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean causeIsKeyUserNotAuthenticated(Exception e) {
|
||||
|
|
|
@ -26,7 +26,7 @@ public class CryptoUtils {
|
|||
public static final byte CRYPTO_NONCE_SIZE = 12;
|
||||
public static final byte CRYPTO_SALT_SIZE = 32;
|
||||
// TODO: decide on a 'secure-enough' iteration count
|
||||
public static final short CRYPTO_ITERATION_COUNT = 10000;
|
||||
public static final short CRYPTO_ITERATION_COUNT = 2000;
|
||||
public static final String CRYPTO_CIPHER_RAW = "AES/ECB/NoPadding";
|
||||
public static final String CRYPTO_CIPHER_AEAD = "AES/GCM/NoPadding";
|
||||
// TODO: use a separate library for an HMAC-SHA256 implementation
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package me.impy.aegis.crypto;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
@ -13,7 +14,7 @@ import javax.crypto.SecretKey;
|
|||
|
||||
import me.impy.aegis.crypto.slots.Slot;
|
||||
|
||||
public class MasterKey {
|
||||
public class MasterKey implements Serializable {
|
||||
private SecretKey _key;
|
||||
|
||||
public MasterKey(SecretKey key) {
|
||||
|
|
89
app/src/main/java/me/impy/aegis/db/DatabaseManager.java
Normal file
89
app/src/main/java/me/impy/aegis/db/DatabaseManager.java
Normal file
|
@ -0,0 +1,89 @@
|
|||
package me.impy.aegis.db;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import me.impy.aegis.KeyProfile;
|
||||
import me.impy.aegis.crypto.CryptParameters;
|
||||
import me.impy.aegis.crypto.CryptResult;
|
||||
import me.impy.aegis.crypto.MasterKey;
|
||||
|
||||
public class DatabaseManager {
|
||||
private MasterKey _key;
|
||||
private DatabaseFile _file;
|
||||
private Database _db;
|
||||
private Context _context;
|
||||
|
||||
public DatabaseManager(Context context) {
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public void load() throws Exception {
|
||||
_file = DatabaseFile.load(_context);
|
||||
if (!_file.isEncrypted()) {
|
||||
byte[] bytes = _file.getContent();
|
||||
_db = new Database();
|
||||
_db.deserialize(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
public void setMasterKey(MasterKey key) throws Exception {
|
||||
byte[] encrypted = _file.getContent();
|
||||
CryptParameters params = _file.getCryptParameters();
|
||||
CryptResult result = key.decrypt(encrypted, params);
|
||||
_db = new Database();
|
||||
_db.deserialize(result.Data);
|
||||
_key = key;
|
||||
}
|
||||
|
||||
public void save() throws Exception {
|
||||
assertDecrypted();
|
||||
byte[] bytes = _db.serialize();
|
||||
CryptResult result = _key.encrypt(bytes);
|
||||
_file.setContent(result.Data);
|
||||
_file.setCryptParameters(result.Parameters);
|
||||
_file.save(_context);
|
||||
}
|
||||
|
||||
public void addKey(KeyProfile profile) throws Exception {
|
||||
assertDecrypted();
|
||||
_db.addKey(profile);
|
||||
}
|
||||
|
||||
public void updateKey(KeyProfile profile) throws Exception {
|
||||
assertDecrypted();
|
||||
_db.updateKey(profile);
|
||||
}
|
||||
|
||||
public void removeKey(KeyProfile profile) throws Exception {
|
||||
assertDecrypted();
|
||||
_db.removeKey(profile);
|
||||
}
|
||||
|
||||
public List<KeyProfile> getKeys() throws Exception {
|
||||
assertDecrypted();
|
||||
return _db.getKeys();
|
||||
}
|
||||
|
||||
public boolean isLoaded() {
|
||||
return _file != null;
|
||||
}
|
||||
|
||||
public boolean isDecrypted() {
|
||||
return _db != null;
|
||||
}
|
||||
|
||||
private void assertLoaded() throws Exception {
|
||||
if (!isLoaded()) {
|
||||
throw new Exception("database file has not been loaded yet");
|
||||
}
|
||||
}
|
||||
|
||||
private void assertDecrypted() throws Exception {
|
||||
assertLoaded();
|
||||
if (!isDecrypted()) {
|
||||
throw new Exception("database file has not been decrypted yet");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,277 +0,0 @@
|
|||
package me.impy.aegis.finger;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.DialogFragment;
|
||||
import android.content.SharedPreferences;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import me.impy.aegis.MainActivity;
|
||||
import me.impy.aegis.R;
|
||||
|
||||
/**
|
||||
* A dialog which uses fingerprint APIs to authenticate the user, and falls back to password
|
||||
* authentication if fingerprint is not available.
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public class FingerprintAuthenticationDialogFragment extends DialogFragment
|
||||
implements TextView.OnEditorActionListener, FingerprintUiHelper.Callback {
|
||||
|
||||
private Button mCancelButton;
|
||||
private Button mSecondDialogButton;
|
||||
private View mFingerprintContent;
|
||||
private View mBackupContent;
|
||||
private EditText mPassword;
|
||||
private CheckBox mUseFingerprintFutureCheckBox;
|
||||
private TextView mPasswordDescriptionTextView;
|
||||
private TextView mNewFingerprintEnrolledTextView;
|
||||
|
||||
private Stage mStage = Stage.FINGERPRINT;
|
||||
private Action mAction;
|
||||
|
||||
private FingerprintManager.CryptoObject mCryptoObject;
|
||||
private FingerprintUiHelper mFingerprintUiHelper;
|
||||
private MainActivity mActivity;
|
||||
|
||||
private InputMethodManager mInputMethodManager;
|
||||
private SharedPreferences mSharedPreferences;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Do not create a new Fragment when the Activity is re-created such as orientation changes.
|
||||
setRetainInstance(true);
|
||||
setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Material_Light_Dialog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
getDialog().setTitle(getString(R.string.sign_in));
|
||||
View v = inflater.inflate(R.layout.fingerprint_dialog_container, container, false);
|
||||
mCancelButton = (Button) v.findViewById(R.id.cancel_button);
|
||||
mCancelButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
mSecondDialogButton = (Button) v.findViewById(R.id.second_dialog_button);
|
||||
mSecondDialogButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (mStage == Stage.FINGERPRINT) {
|
||||
goToBackup();
|
||||
} else {
|
||||
verifyPassword();
|
||||
}
|
||||
}
|
||||
});
|
||||
mFingerprintContent = v.findViewById(R.id.fingerprint_container);
|
||||
mBackupContent = v.findViewById(R.id.backup_container);
|
||||
mPassword = (EditText) v.findViewById(R.id.password);
|
||||
mPassword.setOnEditorActionListener(this);
|
||||
mPasswordDescriptionTextView = (TextView) v.findViewById(R.id.password_description);
|
||||
mUseFingerprintFutureCheckBox = (CheckBox)
|
||||
v.findViewById(R.id.use_fingerprint_in_future_check);
|
||||
mNewFingerprintEnrolledTextView = (TextView)
|
||||
v.findViewById(R.id.new_fingerprint_enrolled_description);
|
||||
mFingerprintUiHelper = new FingerprintUiHelper(
|
||||
mActivity.getSystemService(FingerprintManager.class),
|
||||
(ImageView) v.findViewById(R.id.fingerprint_icon),
|
||||
(TextView) v.findViewById(R.id.fingerprint_status), this);
|
||||
updateStage();
|
||||
|
||||
// If fingerprint authentication is not available, switch immediately to the backup
|
||||
// (password) screen.
|
||||
if (!mFingerprintUiHelper.isFingerprintAuthAvailable()) {
|
||||
goToBackup();
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (mStage == Stage.FINGERPRINT) {
|
||||
mFingerprintUiHelper.startListening(mCryptoObject);
|
||||
}
|
||||
}
|
||||
|
||||
public void setStage(Stage stage) {
|
||||
mStage = stage;
|
||||
}
|
||||
|
||||
public void setAction(Action action) { mAction = action; }
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
mFingerprintUiHelper.stopListening();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
mActivity = (MainActivity) activity;
|
||||
mInputMethodManager = mActivity.getSystemService(InputMethodManager.class);
|
||||
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(mActivity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the crypto object to be passed in when authenticating with fingerprint.
|
||||
*/
|
||||
public void setCryptoObject(FingerprintManager.CryptoObject cryptoObject) {
|
||||
mCryptoObject = cryptoObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches to backup (password) screen. This either can happen when fingerprint is not
|
||||
* available or the user chooses to use the password authentication method by pressing the
|
||||
* button. This can also happen when the user had too many fingerprint attempts.
|
||||
*/
|
||||
private void goToBackup() {
|
||||
mStage = Stage.PASSWORD;
|
||||
updateStage();
|
||||
mPassword.requestFocus();
|
||||
|
||||
// Show the keyboard.
|
||||
mPassword.postDelayed(mShowKeyboardRunnable, 500);
|
||||
|
||||
// Fingerprint is not used anymore. Stop listening for it.
|
||||
mFingerprintUiHelper.stopListening();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the current entered password is correct, and dismisses the the dialog and
|
||||
* let's the activity know about the result.
|
||||
*/
|
||||
private void verifyPassword() {
|
||||
if (!checkPassword(mPassword.getText().toString())) {
|
||||
return;
|
||||
}
|
||||
if (mStage == Stage.NEW_FINGERPRINT_ENROLLED) {
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
editor.putBoolean(getString(R.string.use_fingerprint_to_authenticate_key),
|
||||
mUseFingerprintFutureCheckBox.isChecked());
|
||||
editor.apply();
|
||||
|
||||
if (mUseFingerprintFutureCheckBox.isChecked()) {
|
||||
// Re-create the key so that fingerprints including new ones are validated.
|
||||
//mActivity.createKey(MainActivity.DEFAULT_KEY_NAME, true);
|
||||
mStage = Stage.FINGERPRINT;
|
||||
}
|
||||
}
|
||||
mPassword.setText("");
|
||||
//mActivity.onPurchased(, null);
|
||||
dismiss();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if {@code password} is correct, false otherwise
|
||||
*/
|
||||
private boolean checkPassword(String password) {
|
||||
// Assume the password is always correct.
|
||||
// In the real world situation, the password needs to be verified in the server side.
|
||||
return password.length() > 0;
|
||||
}
|
||||
|
||||
private final Runnable mShowKeyboardRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mInputMethodManager.showSoftInput(mPassword, 0);
|
||||
}
|
||||
};
|
||||
|
||||
private void updateStage() {
|
||||
switch (mStage) {
|
||||
case FINGERPRINT:
|
||||
mCancelButton.setText(R.string.cancel);
|
||||
mSecondDialogButton.setText(R.string.use_password);
|
||||
mFingerprintContent.setVisibility(View.VISIBLE);
|
||||
mBackupContent.setVisibility(View.GONE);
|
||||
break;
|
||||
case NEW_FINGERPRINT_ENROLLED:
|
||||
// Intentional fall through
|
||||
case PASSWORD:
|
||||
mCancelButton.setText(R.string.cancel);
|
||||
mSecondDialogButton.setText(R.string.ok);
|
||||
mFingerprintContent.setVisibility(View.GONE);
|
||||
mBackupContent.setVisibility(View.VISIBLE);
|
||||
if (mStage == Stage.NEW_FINGERPRINT_ENROLLED) {
|
||||
mPasswordDescriptionTextView.setVisibility(View.GONE);
|
||||
mNewFingerprintEnrolledTextView.setVisibility(View.VISIBLE);
|
||||
mUseFingerprintFutureCheckBox.setVisibility(View.VISIBLE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if (actionId == EditorInfo.IME_ACTION_GO) {
|
||||
verifyPassword();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticated() {
|
||||
// Callback from FingerprintUiHelper. Let the activity know that authentication was
|
||||
// successful.
|
||||
mActivity.onAuthenticated(mAction, mCryptoObject);
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError() {
|
||||
goToBackup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumeration to indicate which authentication method the user is trying to authenticate with.
|
||||
*/
|
||||
public enum Stage {
|
||||
FINGERPRINT,
|
||||
NEW_FINGERPRINT_ENROLLED,
|
||||
PASSWORD
|
||||
}
|
||||
|
||||
public enum Action {
|
||||
LOAD,
|
||||
SAVE
|
||||
}
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
package me.impy.aegis.finger;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.DialogFragment;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import me.impy.aegis.CustomAuthenticationSlide;
|
||||
import me.impy.aegis.IntroActivity;
|
||||
import me.impy.aegis.MainActivity;
|
||||
import me.impy.aegis.R;
|
||||
|
||||
/**
|
||||
* A dialog which uses fingerprint APIs to authenticate the user, and falls back to password
|
||||
* authentication if fingerprint is not available.
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public class SetFingerprintAuthenticationDialog extends DialogFragment implements FingerprintUiHelper.Callback {
|
||||
|
||||
private Button mCancelButton;
|
||||
private Button mSecondDialogButton;
|
||||
private View mFingerprintContent;
|
||||
private View mBackupContent;
|
||||
private EditText mPassword;
|
||||
private CheckBox mUseFingerprintFutureCheckBox;
|
||||
private TextView mPasswordDescriptionTextView;
|
||||
private TextView mNewFingerprintEnrolledTextView;
|
||||
|
||||
private Stage mStage = Stage.FINGERPRINT;
|
||||
private Action mAction;
|
||||
|
||||
private FingerprintManager.CryptoObject mCryptoObject;
|
||||
private FingerprintUiHelper mFingerprintUiHelper;
|
||||
private IntroActivity mIntroActivity;
|
||||
public InterfaceCommunicator interfaceCommunicator;
|
||||
|
||||
private InputMethodManager mInputMethodManager;
|
||||
private SharedPreferences mSharedPreferences;
|
||||
private CustomAuthenticationSlide customAuthenticationSlide;
|
||||
|
||||
public void setCaller(CustomAuthenticationSlide customAuthenticationSlide)
|
||||
{
|
||||
this.customAuthenticationSlide = customAuthenticationSlide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setRetainInstance(true);
|
||||
setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Material_Light_Dialog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
getDialog().setTitle(getString(R.string.sign_in));
|
||||
View v = inflater.inflate(R.layout.fingerprint_dialog_container, container, false);
|
||||
mCancelButton = (Button) v.findViewById(R.id.cancel_button);
|
||||
mCancelButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
customAuthenticationSlide.sendRequestCode(0);
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
mFingerprintContent = v.findViewById(R.id.fingerprint_container);
|
||||
mFingerprintUiHelper = new FingerprintUiHelper(
|
||||
mIntroActivity.getSystemService(FingerprintManager.class),
|
||||
(ImageView) v.findViewById(R.id.fingerprint_icon),
|
||||
(TextView) v.findViewById(R.id.fingerprint_status), this);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (mStage == Stage.FINGERPRINT) {
|
||||
mFingerprintUiHelper.startListening(mCryptoObject);
|
||||
}
|
||||
}
|
||||
|
||||
public void setStage(Stage stage) {
|
||||
mStage = stage;
|
||||
}
|
||||
|
||||
public void setAction(Action action) { mAction = action; }
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
mFingerprintUiHelper.stopListening();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
super.onDismiss(dialog);
|
||||
customAuthenticationSlide.sendRequestCode(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
mIntroActivity = (IntroActivity) activity;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticated() {
|
||||
// Callback from FingerprintUiHelper. Let the activity know that authentication was
|
||||
// successful.
|
||||
customAuthenticationSlide.sendRequestCode(1);
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError() {
|
||||
customAuthenticationSlide.sendRequestCode(0);
|
||||
}
|
||||
|
||||
public interface InterfaceCommunicator {
|
||||
void sendRequestCode(int code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumeration to indicate which authentication method the user is trying to authenticate with.
|
||||
*/
|
||||
public enum Stage {
|
||||
FINGERPRINT,
|
||||
NEW_FINGERPRINT_ENROLLED,
|
||||
PASSWORD
|
||||
}
|
||||
|
||||
public enum Action {
|
||||
LOAD,
|
||||
SAVE
|
||||
}
|
||||
}
|
|
@ -42,7 +42,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword"
|
||||
android:ems="10"
|
||||
android:id="@+id/editText2"
|
||||
android:id="@+id/text_password"
|
||||
android:layout_below="@+id/textView3"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginTop="10dp"
|
||||
|
@ -62,7 +62,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword"
|
||||
android:ems="10"
|
||||
android:id="@+id/editText"
|
||||
android:id="@+id/text_password_confirm"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_below="@+id/textView4"
|
||||
android:layout_alignParentStart="true"
|
||||
|
|
Loading…
Add table
Reference in a new issue