mirror of https://github.com/beemdevelopment/Aegis
Add an activity to manage database key slots
parent
d5f796ca87
commit
c24b691a26
@ -0,0 +1,79 @@
|
|||||||
|
package me.impy.aegis;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.hardware.fingerprint.FingerprintManager;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.mattprecious.swirl.SwirlView;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
|
||||||
|
import me.impy.aegis.crypto.KeyStoreHandle;
|
||||||
|
import me.impy.aegis.crypto.slots.FingerprintSlot;
|
||||||
|
import me.impy.aegis.crypto.slots.Slot;
|
||||||
|
import me.impy.aegis.helpers.FingerprintHelper;
|
||||||
|
import me.impy.aegis.helpers.FingerprintUiHelper;
|
||||||
|
|
||||||
|
public class FingerprintDialogFragment extends SlotDialogFragment implements FingerprintUiHelper.Callback {
|
||||||
|
private Cipher _cipher;
|
||||||
|
private FingerprintUiHelper _helper;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_fingerprint, null);
|
||||||
|
TextView textFingerprint = view.findViewById(R.id.text_fingerprint);
|
||||||
|
SwirlView imgFingerprint = view.findViewById(R.id.img_fingerprint);
|
||||||
|
|
||||||
|
FingerprintManager manager = FingerprintHelper.getManager(getContext());
|
||||||
|
try {
|
||||||
|
KeyStoreHandle handle = new KeyStoreHandle();
|
||||||
|
SecretKey key = handle.getKey();
|
||||||
|
_cipher = Slot.createCipher(key, Cipher.ENCRYPT_MODE);
|
||||||
|
_helper = new FingerprintUiHelper(manager, imgFingerprint, textFingerprint, this);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AlertDialog.Builder(getActivity())
|
||||||
|
.setTitle("Register a new fingerprint")
|
||||||
|
.setView(view)
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
if (_helper != null) {
|
||||||
|
_helper.startListening(new FingerprintManager.CryptoObject(_cipher));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
|
||||||
|
if (_helper != null) {
|
||||||
|
_helper.stopListening();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticated() {
|
||||||
|
FingerprintSlot slot = new FingerprintSlot();
|
||||||
|
getListener().onSlotResult(slot, _cipher);
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
package me.impy.aegis;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
|
||||||
|
import me.impy.aegis.crypto.slots.PasswordSlot;
|
||||||
|
import me.impy.aegis.crypto.slots.Slot;
|
||||||
|
import me.impy.aegis.helpers.AuthHelper;
|
||||||
|
|
||||||
|
public class PasswordDialogFragment extends SlotDialogFragment {
|
||||||
|
private Button _buttonOK;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_password, null);
|
||||||
|
EditText textPassword = view.findViewById(R.id.text_password);
|
||||||
|
EditText textPasswordConfirm = view.findViewById(R.id.text_password_confirm);
|
||||||
|
|
||||||
|
AlertDialog alert = new AlertDialog.Builder(getActivity())
|
||||||
|
.setTitle("Enter a new password")
|
||||||
|
.setView(view)
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
alert.setOnShowListener(dialog -> {
|
||||||
|
_buttonOK = alert.getButton(AlertDialog.BUTTON_POSITIVE);
|
||||||
|
_buttonOK.setEnabled(false);
|
||||||
|
|
||||||
|
// replace the default listener
|
||||||
|
_buttonOK.setOnClickListener(v -> {
|
||||||
|
if (!AuthHelper.arePasswordsEqual(textPassword, textPasswordConfirm)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthHelper.clearPassword(textPasswordConfirm);
|
||||||
|
char[] password = AuthHelper.getPassword(textPassword, true);
|
||||||
|
|
||||||
|
PasswordSlot slot = new PasswordSlot();
|
||||||
|
DerivationTask task = new DerivationTask(getContext(), key -> {
|
||||||
|
Cipher cipher;
|
||||||
|
try {
|
||||||
|
cipher = Slot.createCipher(key, Cipher.ENCRYPT_MODE);
|
||||||
|
} catch (Exception e) {
|
||||||
|
getListener().onException(e);
|
||||||
|
dialog.cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
getListener().onSlotResult(slot, cipher);
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
task.execute(new DerivationTask.Params() {{
|
||||||
|
Slot = slot;
|
||||||
|
Password = password;
|
||||||
|
}});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
TextWatcher watcher = new TextWatcher() {
|
||||||
|
public void onTextChanged(CharSequence c, int start, int before, int count) {
|
||||||
|
boolean equal = AuthHelper.arePasswordsEqual(textPassword, textPasswordConfirm);
|
||||||
|
_buttonOK.setEnabled(equal);
|
||||||
|
}
|
||||||
|
public void beforeTextChanged(CharSequence c, int start, int count, int after) { }
|
||||||
|
public void afterTextChanged(Editable c) { }
|
||||||
|
};
|
||||||
|
textPassword.addTextChangedListener(watcher);
|
||||||
|
textPasswordConfirm.addTextChangedListener(watcher);
|
||||||
|
|
||||||
|
return alert;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
package me.impy.aegis;
|
||||||
|
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import me.impy.aegis.crypto.slots.Slot;
|
||||||
|
|
||||||
|
public class SlotAdapter extends RecyclerView.Adapter<SlotHolder> {
|
||||||
|
private Listener _listener;
|
||||||
|
private ArrayList<Slot> _slots;
|
||||||
|
|
||||||
|
public SlotAdapter(Listener listener) {
|
||||||
|
_listener = listener;
|
||||||
|
_slots = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSlot(Slot slot) {
|
||||||
|
_slots.add(slot);
|
||||||
|
|
||||||
|
int position = getItemCount() - 1;
|
||||||
|
if (position == 0) {
|
||||||
|
notifyDataSetChanged();
|
||||||
|
} else {
|
||||||
|
notifyItemInserted(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateSlot(Slot slot) {
|
||||||
|
notifyItemChanged(_slots.indexOf(slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeSlot(Slot slot) {
|
||||||
|
int position = _slots.indexOf(slot);
|
||||||
|
_slots.remove(position);
|
||||||
|
notifyItemRemoved(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SlotHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_slot, parent, false);
|
||||||
|
return new SlotHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(SlotHolder holder, int position) {
|
||||||
|
holder.setData(_slots.get(position));
|
||||||
|
holder.setOnEditClickListener(v -> {
|
||||||
|
int position1 = holder.getAdapterPosition();
|
||||||
|
_listener.onEditSlot(_slots.get(position1));
|
||||||
|
});
|
||||||
|
holder.setOnDeleteClickListener(v -> {
|
||||||
|
int position12 = holder.getAdapterPosition();
|
||||||
|
_listener.onRemoveSlot(_slots.get(position12));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return _slots.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Listener {
|
||||||
|
void onEditSlot(Slot slot);
|
||||||
|
void onRemoveSlot(Slot slot);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package me.impy.aegis;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.v4.app.DialogFragment;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
|
||||||
|
import me.impy.aegis.crypto.slots.Slot;
|
||||||
|
|
||||||
|
public class SlotDialogFragment extends DialogFragment {
|
||||||
|
private Listener _listener;
|
||||||
|
|
||||||
|
protected Listener getListener() {
|
||||||
|
return _listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
|
||||||
|
try {
|
||||||
|
_listener = (Listener) context;
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
throw new ClassCastException(context.toString() + " must implement SlotDialogFragment.Listener");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Listener {
|
||||||
|
void onSlotResult(Slot slot, Cipher cipher);
|
||||||
|
void onException(Exception e);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package me.impy.aegis;
|
||||||
|
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class SlotHolder extends RecyclerView.ViewHolder {
|
||||||
|
private TextView _slotUsed;
|
||||||
|
private TextView _slotName;
|
||||||
|
private ImageView _slotImg;
|
||||||
|
private LinearLayout _buttonEdit;
|
||||||
|
private ImageView _buttonDelete;
|
||||||
|
|
||||||
|
public SlotHolder(final View view) {
|
||||||
|
super(view);
|
||||||
|
_slotUsed = view.findViewById(R.id.text_slot_used);
|
||||||
|
_slotName = view.findViewById(R.id.text_slot_name);
|
||||||
|
_slotImg = view.findViewById(R.id.img_slot);
|
||||||
|
_buttonEdit = view.findViewById(R.id.button_edit);
|
||||||
|
_buttonDelete = view.findViewById(R.id.button_delete);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(Slot slot) {
|
||||||
|
if (slot instanceof PasswordSlot) {
|
||||||
|
_slotName.setText("Password 1");
|
||||||
|
_slotImg.setImageResource(R.drawable.ic_create_black_24dp);
|
||||||
|
} else if (slot instanceof FingerprintSlot) {
|
||||||
|
_slotName.setText("Finger 1");
|
||||||
|
_slotImg.setImageResource(R.drawable.ic_fingerprint_black_24dp);
|
||||||
|
} else if (slot instanceof RawSlot) {
|
||||||
|
_slotName.setText("Raw 1");
|
||||||
|
_slotImg.setImageResource(R.drawable.ic_vpn_key_black_24dp);
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnEditClickListener(View.OnClickListener listener) {
|
||||||
|
_buttonEdit.setOnClickListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnDeleteClickListener(View.OnClickListener listener) {
|
||||||
|
_buttonDelete.setOnClickListener(listener);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,156 @@
|
|||||||
|
package me.impy.aegis;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.app.ActionBar;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
|
||||||
|
import me.impy.aegis.crypto.MasterKey;
|
||||||
|
import me.impy.aegis.crypto.slots.PasswordSlot;
|
||||||
|
import me.impy.aegis.crypto.slots.Slot;
|
||||||
|
import me.impy.aegis.crypto.slots.SlotCollection;
|
||||||
|
import me.impy.aegis.helpers.FingerprintHelper;
|
||||||
|
|
||||||
|
public class SlotManagerActivity extends AegisActivity implements SlotAdapter.Listener, SlotDialogFragment.Listener {
|
||||||
|
private MasterKey _masterKey;
|
||||||
|
private SlotCollection _slots;
|
||||||
|
private SlotAdapter _adapter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// set up the view
|
||||||
|
setContentView(R.layout.activity_slots);
|
||||||
|
setSupportActionBar(findViewById(R.id.toolbar));
|
||||||
|
ActionBar bar = getSupportActionBar();
|
||||||
|
bar.setHomeAsUpIndicator(R.drawable.ic_close);
|
||||||
|
bar.setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
|
// only show the fingerprint option if we can get an instance of the fingerprint manager
|
||||||
|
// TODO: also hide the option if this device's fingerprint has already been registered
|
||||||
|
if (FingerprintHelper.getManager(this) != null) {
|
||||||
|
findViewById(R.id.button_add_fingerprint).setOnClickListener(view -> {
|
||||||
|
FingerprintDialogFragment dialog = new FingerprintDialogFragment();
|
||||||
|
dialog.show(getSupportFragmentManager(), null);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
findViewById(R.id.button_add_fingerprint).setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
findViewById(R.id.button_add_password).setOnClickListener(view -> {
|
||||||
|
PasswordDialogFragment dialog = new PasswordDialogFragment();
|
||||||
|
dialog.show(getSupportFragmentManager(), null);
|
||||||
|
});
|
||||||
|
|
||||||
|
// set up the recycler view
|
||||||
|
_adapter = new SlotAdapter(this);
|
||||||
|
RecyclerView slotsView = findViewById(R.id.list_slots);
|
||||||
|
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
|
||||||
|
slotsView.setLayoutManager(layoutManager);
|
||||||
|
slotsView.setAdapter(_adapter);
|
||||||
|
slotsView.setNestedScrollingEnabled(false);
|
||||||
|
|
||||||
|
// load the slots and masterKey
|
||||||
|
_masterKey = (MasterKey) getIntent().getSerializableExtra("masterKey");
|
||||||
|
_slots = (SlotCollection) getIntent().getSerializableExtra("slots");
|
||||||
|
for (Slot slot : _slots) {
|
||||||
|
_adapter.addSlot(slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean onSave() {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.putExtra("slots", _slots);
|
||||||
|
setResult(RESULT_OK, intent);
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setPreferredTheme(boolean nightMode) {
|
||||||
|
if (nightMode) {
|
||||||
|
setTheme(R.style.AppTheme_Dark_NoActionBar);
|
||||||
|
} else {
|
||||||
|
setTheme(R.style.AppTheme_Default_NoActionBar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case android.R.id.home:
|
||||||
|
onBackPressed();
|
||||||
|
return true;
|
||||||
|
case R.id.action_save:
|
||||||
|
return onSave();
|
||||||
|
default:
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.menu_slots, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEditSlot(Slot slot) {
|
||||||
|
EditText textName = new EditText(this);
|
||||||
|
textName.setHint("Name");
|
||||||
|
|
||||||
|
new AlertDialog.Builder(this)
|
||||||
|
.setTitle("Edit slot name")
|
||||||
|
.setView(textName)
|
||||||
|
.setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
|
||||||
|
String name = textName.getText().toString();
|
||||||
|
})
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRemoveSlot(Slot slot) {
|
||||||
|
if (slot instanceof PasswordSlot && _slots.findAll(PasswordSlot.class).size() <= 1) {
|
||||||
|
Toast.makeText(this, "You must have at least one password slot", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new AlertDialog.Builder(this)
|
||||||
|
.setTitle("Remove slot")
|
||||||
|
.setMessage("Are you sure you want to remove this slot?")
|
||||||
|
.setPositiveButton(android.R.string.yes, (dialog, whichButton) -> {
|
||||||
|
_slots.remove(slot);
|
||||||
|
_adapter.removeSlot(slot);
|
||||||
|
})
|
||||||
|
.setNegativeButton(android.R.string.no, null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSlotResult(Slot slot, Cipher cipher) {
|
||||||
|
try {
|
||||||
|
_slots.encrypt(slot, _masterKey, cipher);
|
||||||
|
} catch (Exception e) {
|
||||||
|
onException(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_slots.add(slot);
|
||||||
|
_adapter.addSlot(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onException(Exception e) {
|
||||||
|
Toast.makeText(this, "An error occurred while trying to add a new slot: " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:width="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path android:fillColor="#000" android:pathData="M11.83,1.73C8.43,1.79 6.23,3.32 6.23,3.32C5.95,3.5 5.88,3.91 6.07,4.19C6.27,4.5 6.66,4.55 6.96,4.34C6.96,4.34 11.27,1.15 17.46,4.38C17.75,4.55 18.14,4.45 18.31,4.15C18.5,3.85 18.37,3.47 18.03,3.28C16.36,2.4 14.78,1.96 13.36,1.8C12.83,1.74 12.32,1.72 11.83,1.73M12.22,4.34C6.26,4.26 3.41,9.05 3.41,9.05C3.22,9.34 3.3,9.72 3.58,9.91C3.87,10.1 4.26,10 4.5,9.68C4.5,9.68 6.92,5.5 12.2,5.59C17.5,5.66 19.82,9.65 19.82,9.65C20,9.94 20.38,10.04 20.68,9.87C21,9.69 21.07,9.31 20.9,9C20.9,9 18.15,4.42 12.22,4.34M11.5,6.82C9.82,6.94 8.21,7.55 7,8.56C4.62,10.53 3.1,14.14 4.77,19C4.88,19.33 5.24,19.5 5.57,19.39C5.89,19.28 6.07,18.92 5.95,18.6V18.6C4.41,14.13 5.78,11.2 7.8,9.5C9.77,7.89 13.25,7.5 15.84,9.1C17.11,9.9 18.1,11.28 18.6,12.64C19.11,14 19.08,15.32 18.67,15.94C18.25,16.59 17.4,16.83 16.65,16.64C15.9,16.45 15.29,15.91 15.26,14.77C15.23,13.06 13.89,12 12.5,11.84C11.16,11.68 9.61,12.4 9.21,14C8.45,16.92 10.36,21.07 14.78,22.45C15.11,22.55 15.46,22.37 15.57,22.04C15.67,21.71 15.5,21.35 15.15,21.25C11.32,20.06 9.87,16.43 10.42,14.29C10.66,13.33 11.5,13 12.38,13.08C13.25,13.18 14,13.7 14,14.79C14.05,16.43 15.12,17.54 16.34,17.85C17.56,18.16 18.97,17.77 19.72,16.62C20.5,15.45 20.37,13.8 19.78,12.21C19.18,10.61 18.07,9.03 16.5,8.04C14.96,7.08 13.19,6.7 11.5,6.82M11.86,9.25V9.26C10.08,9.32 8.3,10.24 7.28,12.18C5.96,14.67 6.56,17.21 7.44,19.04C8.33,20.88 9.54,22.1 9.54,22.1C9.78,22.35 10.17,22.35 10.42,22.11C10.67,21.87 10.67,21.5 10.43,21.23C10.43,21.23 9.36,20.13 8.57,18.5C7.78,16.87 7.3,14.81 8.38,12.77C9.5,10.67 11.5,10.16 13.26,10.67C15.04,11.19 16.53,12.74 16.5,15.03C16.46,15.38 16.71,15.68 17.06,15.7C17.4,15.73 17.7,15.47 17.73,15.06C17.79,12.2 15.87,10.13 13.61,9.47C13.04,9.31 12.45,9.23 11.86,9.25M12.08,14.25C11.73,14.26 11.46,14.55 11.47,14.89C11.47,14.89 11.5,16.37 12.31,17.8C13.15,19.23 14.93,20.59 18.03,20.3C18.37,20.28 18.64,20 18.62,19.64C18.6,19.29 18.3,19.03 17.91,19.06C15.19,19.31 14.04,18.28 13.39,17.17C12.74,16.07 12.72,14.88 12.72,14.88C12.72,14.53 12.44,14.25 12.08,14.25Z" />
|
||||||
|
</vector>
|
@ -0,0 +1,7 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:width="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path android:fillColor="#000" android:pathData="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z" />
|
||||||
|
</vector>
|
@ -0,0 +1,126 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
tools:context="me.impy.aegis.SlotManagerActivity">
|
||||||
|
|
||||||
|
<android.support.design.widget.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:theme="@style/AppTheme.AppBarOverlay">
|
||||||
|
|
||||||
|
<android.support.v7.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||||
|
|
||||||
|
</android.support.design.widget.AppBarLayout>
|
||||||
|
|
||||||
|
<android.support.v4.widget.NestedScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<android.support.v7.widget.RecyclerView
|
||||||
|
android:id="@+id/list_slots"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:scrollbars="vertical"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/button_add_password"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="12.5dp"
|
||||||
|
android:paddingBottom="12.5dp"
|
||||||
|
android:paddingStart="10dp"
|
||||||
|
android:paddingEnd="10dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:foreground="?android:attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@drawable/ic_plus_black_24dp"
|
||||||
|
android:tint="@color/colorAccent"
|
||||||
|
android:layout_marginEnd="15dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Add password"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/button_add_fingerprint"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="12.5dp"
|
||||||
|
android:paddingBottom="12.5dp"
|
||||||
|
android:paddingStart="10dp"
|
||||||
|
android:paddingEnd="10dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:foreground="?android:attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@drawable/ic_plus_black_24dp"
|
||||||
|
android:tint="@color/colorAccent"
|
||||||
|
android:layout_marginEnd="15dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Add fingerprint"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="@android:color/darker_gray"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="12.5dp"
|
||||||
|
android:paddingBottom="12.5dp"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginEnd="10dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@drawable/ic_info_outline_black_24dp"
|
||||||
|
android:layout_marginEnd="15dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="The database is only as secure as your weakest key.\n\nWhen a new fingerprint is enrolled, all keys are wiped from the keystore. You should always have at least one password slot to prevent accidental data loss."/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</android.support.v4.widget.NestedScrollView>
|
||||||
|
|
||||||
|
</android.support.design.widget.CoordinatorLayout>
|
@ -0,0 +1,66 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/button_edit"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="10dp"
|
||||||
|
android:paddingEnd="10dp"
|
||||||
|
android:paddingTop="12.5dp"
|
||||||
|
android:paddingBottom="12.5dp"
|
||||||
|
android:foreground="?android:attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/img_slot"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginEnd="15dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:text="Slot name"
|
||||||
|
android:id="@+id/text_slot_name"
|
||||||
|
android:textColor="@color/primary_text"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_slot_used"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
</TextView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="1dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@android:color/darker_gray"
|
||||||
|
android:paddingStart="15dp"
|
||||||
|
android:paddingEnd="15dp"
|
||||||
|
android:layout_marginTop="12.5dp"
|
||||||
|
android:layout_marginBottom="12.5dp"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/button_delete"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:src="@drawable/ic_delete_black_24dp"
|
||||||
|
android:paddingStart="15dp"
|
||||||
|
android:paddingEnd="15dp"
|
||||||
|
android:paddingTop="12.5dp"
|
||||||
|
android:paddingBottom="12.5dp"
|
||||||
|
android:foreground="?android:attr/selectableItemBackground"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="20dp"
|
||||||
|
android:paddingEnd="20dp"
|
||||||
|
android:paddingTop="20dp">
|
||||||
|
<com.mattprecious.swirl.SwirlView
|
||||||
|
android:id="@+id/img_fingerprint"
|
||||||
|
android:layout_width="60dp"
|
||||||
|
android:layout_height="60dp"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_fingerprint"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="15dp"
|
||||||
|
android:text="@string/fingerprint_hint"
|
||||||
|
android:textColor="?attr/secondaryText"
|
||||||
|
android:layout_gravity="center_vertical"/>
|
||||||
|
</LinearLayout>
|
@ -1,19 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingBottom="16dp"
|
|
||||||
android:paddingTop="16dp">
|
|
||||||
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inputType="textPersonName"
|
|
||||||
android:text="Name"
|
|
||||||
android:ems="10"
|
|
||||||
android:id="@+id/profile_name"
|
|
||||||
android:layout_weight="0.96"/>
|
|
||||||
</LinearLayout>
|
|
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingStart="20dp"
|
||||||
|
android:paddingEnd="20dp"
|
||||||
|
android:paddingTop="20dp">
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/text_password"
|
||||||
|
android:hint="Password"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
<EditText
|
||||||
|
android:hint="Confirm password"
|
||||||
|
android:id="@+id/text_password_confirm"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
tools:context="me.impy.aegis.SlotManagerActivity">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_save"
|
||||||
|
app:showAsAction="ifRoom"
|
||||||
|
android:title="@string/save"/>
|
||||||
|
</menu>
|
Loading…
Reference in New Issue