2016-08-15 21:29:41 +02:00
|
|
|
package me.impy.aegis;
|
|
|
|
|
2016-08-21 22:32:07 +02:00
|
|
|
import android.content.ClipData;
|
|
|
|
import android.content.ClipboardManager;
|
2016-08-21 22:24:04 +02:00
|
|
|
import android.content.Context;
|
2016-08-15 22:31:28 +02:00
|
|
|
import android.content.Intent;
|
2016-09-29 12:31:55 +02:00
|
|
|
import android.content.SharedPreferences;
|
2016-11-03 22:03:34 +01:00
|
|
|
import android.content.pm.ShortcutInfo;
|
|
|
|
import android.content.pm.ShortcutManager;
|
|
|
|
import android.graphics.drawable.Icon;
|
|
|
|
import android.net.Uri;
|
|
|
|
import android.os.Build;
|
2016-11-13 18:21:00 +01:00
|
|
|
import android.hardware.fingerprint.FingerprintManager;
|
2016-09-30 01:08:03 +02:00
|
|
|
import android.preference.PreferenceManager;
|
2016-11-01 22:16:54 +01:00
|
|
|
import android.support.design.widget.BottomSheetDialog;
|
2016-08-16 14:14:17 +02:00
|
|
|
import android.support.design.widget.FloatingActionButton;
|
2016-11-01 22:57:21 +01:00
|
|
|
import android.support.v7.app.AlertDialog;
|
2016-08-15 21:29:41 +02:00
|
|
|
import android.support.v7.app.AppCompatActivity;
|
|
|
|
import android.os.Bundle;
|
2016-08-16 20:04:38 +02:00
|
|
|
import android.support.v7.widget.LinearLayoutManager;
|
|
|
|
import android.support.v7.widget.RecyclerView;
|
2016-08-16 14:14:17 +02:00
|
|
|
import android.support.v7.widget.Toolbar;
|
2016-08-21 22:54:27 +02:00
|
|
|
import android.support.v7.widget.helper.ItemTouchHelper;
|
2016-11-13 18:00:13 +01:00
|
|
|
import android.util.Log;
|
2016-08-16 14:14:17 +02:00
|
|
|
import android.view.Menu;
|
|
|
|
import android.view.MenuItem;
|
2016-11-01 22:16:54 +01:00
|
|
|
import android.view.View;
|
|
|
|
import android.widget.LinearLayout;
|
2016-08-17 01:14:25 +02:00
|
|
|
import android.widget.Toast;
|
|
|
|
|
2016-11-13 18:21:00 +01:00
|
|
|
import java.io.IOException;
|
2016-08-16 20:04:38 +02:00
|
|
|
import java.util.ArrayList;
|
2016-11-03 22:03:34 +01:00
|
|
|
import java.util.Arrays;
|
2016-10-25 23:53:33 +02:00
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.Comparator;
|
2016-11-13 18:00:13 +01:00
|
|
|
import java.util.Objects;
|
2016-08-16 20:04:38 +02:00
|
|
|
|
2016-11-13 18:21:00 +01:00
|
|
|
import javax.crypto.Cipher;
|
|
|
|
import javax.crypto.IllegalBlockSizeException;
|
|
|
|
import javax.crypto.SecretKey;
|
|
|
|
|
|
|
|
import me.impy.aegis.crypto.CryptParameters;
|
|
|
|
import me.impy.aegis.crypto.CryptResult;
|
2016-08-22 19:31:03 +02:00
|
|
|
import me.impy.aegis.crypto.CryptoUtils;
|
2016-11-13 18:21:00 +01:00
|
|
|
import me.impy.aegis.crypto.DerivationParameters;
|
|
|
|
import me.impy.aegis.crypto.KeyStoreHandle;
|
2016-08-16 13:31:22 +02:00
|
|
|
import me.impy.aegis.crypto.OTP;
|
2016-08-22 19:31:03 +02:00
|
|
|
import me.impy.aegis.db.Database;
|
2016-11-13 18:21:00 +01:00
|
|
|
import me.impy.aegis.db.DatabaseFile;
|
|
|
|
import me.impy.aegis.finger.FingerprintAuthenticationDialogFragment;
|
2016-10-04 22:23:34 +02:00
|
|
|
import me.impy.aegis.helpers.SimpleItemTouchHelperCallback;
|
2016-08-15 21:29:41 +02:00
|
|
|
|
2016-11-13 18:21:00 +01:00
|
|
|
public class MainActivity extends AppCompatActivity {
|
2016-08-15 21:29:41 +02:00
|
|
|
|
2016-08-16 00:08:01 +02:00
|
|
|
static final int GET_KEYINFO = 1;
|
2016-08-24 23:48:25 +02:00
|
|
|
static final int ADD_KEYINFO = 2;
|
|
|
|
|
2016-08-16 20:04:38 +02:00
|
|
|
RecyclerView rvKeyProfiles;
|
|
|
|
KeyProfileAdapter mKeyProfileAdapter;
|
2016-11-13 18:21:00 +01:00
|
|
|
ArrayList<KeyProfile> mKeyProfiles = new ArrayList<>();
|
2016-08-22 19:31:03 +02:00
|
|
|
Database database;
|
2016-11-13 18:21:00 +01:00
|
|
|
DatabaseFile databaseFile;
|
2016-09-30 01:08:03 +02:00
|
|
|
|
2016-11-13 18:21:00 +01:00
|
|
|
boolean blockSave = false;
|
2016-09-30 01:08:03 +02:00
|
|
|
boolean nightMode = false;
|
2016-11-01 22:16:54 +01:00
|
|
|
int clickedItemPosition = -1;
|
2016-08-16 14:14:17 +02:00
|
|
|
|
2016-08-15 21:29:41 +02:00
|
|
|
@Override
|
|
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
|
|
super.onCreate(savedInstanceState);
|
2016-09-29 12:31:55 +02:00
|
|
|
|
|
|
|
SharedPreferences prefs = this.getSharedPreferences("me.impy.aegis", Context.MODE_PRIVATE);
|
2016-11-13 18:21:00 +01:00
|
|
|
if (!prefs.getBoolean("passedIntro", false)) {
|
2016-09-29 12:31:55 +02:00
|
|
|
Intent intro = new Intent(this, IntroActivity.class);
|
|
|
|
startActivity(intro);
|
|
|
|
}
|
|
|
|
|
2016-09-30 01:08:03 +02:00
|
|
|
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
2016-11-13 18:21:00 +01:00
|
|
|
if (sharedPreferences.getBoolean("pref_night_mode", false)) {
|
2016-09-30 01:08:03 +02:00
|
|
|
nightMode = true;
|
|
|
|
setTheme(R.style.AppTheme_Dark_NoActionBar);
|
2016-11-13 18:21:00 +01:00
|
|
|
} else {
|
2016-09-30 01:08:03 +02:00
|
|
|
setPreferredTheme();
|
|
|
|
}
|
|
|
|
|
2016-08-15 21:29:41 +02:00
|
|
|
setContentView(R.layout.activity_main);
|
2016-08-16 14:14:17 +02:00
|
|
|
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
|
|
|
setSupportActionBar(toolbar);
|
2016-11-03 22:03:34 +01:00
|
|
|
initializeAppShortcuts();
|
2016-08-16 14:14:17 +02:00
|
|
|
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
|
2016-11-13 18:21:00 +01:00
|
|
|
fab.setEnabled(false);
|
2016-10-26 00:07:39 +02:00
|
|
|
fab.setOnClickListener(view -> {
|
2016-11-13 18:21:00 +01:00
|
|
|
blockSave = true;
|
2016-10-26 00:07:39 +02:00
|
|
|
Intent scannerActivity = new Intent(getApplicationContext(), ScannerActivity.class);
|
|
|
|
startActivityForResult(scannerActivity, GET_KEYINFO);
|
2016-08-15 22:31:28 +02:00
|
|
|
});
|
2016-08-16 14:14:17 +02:00
|
|
|
|
2016-08-16 20:04:38 +02:00
|
|
|
rvKeyProfiles = (RecyclerView) findViewById(R.id.rvKeyProfiles);
|
|
|
|
LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
|
|
|
|
rvKeyProfiles.setLayoutManager(mLayoutManager);
|
|
|
|
|
2016-08-21 23:13:27 +02:00
|
|
|
mKeyProfileAdapter = new KeyProfileAdapter(mKeyProfiles);
|
2016-10-26 00:29:21 +02:00
|
|
|
mKeyProfileAdapter.setOnItemClickListener((position, v) -> {
|
2016-11-01 22:16:54 +01:00
|
|
|
clickedItemPosition = position;
|
|
|
|
InitializeBottomSheet().show();
|
|
|
|
});
|
2016-10-28 13:18:11 +02:00
|
|
|
mKeyProfileAdapter.setOnLongItemClickListener((position, v) -> {
|
2016-11-01 22:16:54 +01:00
|
|
|
|
2016-10-28 13:18:11 +02:00
|
|
|
});
|
|
|
|
|
2016-10-04 22:23:34 +02:00
|
|
|
ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(mKeyProfileAdapter);
|
|
|
|
ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
|
|
|
|
touchHelper.attachToRecyclerView(rvKeyProfiles);
|
2016-08-21 22:54:27 +02:00
|
|
|
|
2016-08-16 20:04:38 +02:00
|
|
|
rvKeyProfiles.setAdapter(mKeyProfileAdapter);
|
2016-10-25 23:53:33 +02:00
|
|
|
Comparator<KeyProfile> comparator = new Comparator<KeyProfile>() {
|
|
|
|
@Override
|
|
|
|
public int compare(KeyProfile keyProfile, KeyProfile t1) {
|
|
|
|
return keyProfile.Order - t1.Order;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Collections.sort(mKeyProfiles, comparator);
|
2016-11-13 18:21:00 +01:00
|
|
|
|
|
|
|
loadDatabase(null);
|
2016-08-15 21:29:41 +02:00
|
|
|
}
|
2016-08-16 00:08:01 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
2016-11-13 18:21:00 +01:00
|
|
|
blockSave = false;
|
2016-08-16 00:08:01 +02:00
|
|
|
if (requestCode == GET_KEYINFO) {
|
|
|
|
if (resultCode == RESULT_OK) {
|
2016-08-17 01:14:25 +02:00
|
|
|
final KeyProfile keyProfile = (KeyProfile)data.getSerializableExtra("KeyProfile");
|
2016-08-16 00:08:01 +02:00
|
|
|
|
2016-08-24 01:26:33 +02:00
|
|
|
Intent intent = new Intent(this, AddProfileActivity.class);
|
2016-08-24 23:48:25 +02:00
|
|
|
intent.putExtra("KeyProfile", keyProfile);
|
|
|
|
startActivityForResult(intent, ADD_KEYINFO);
|
2016-08-16 00:08:01 +02:00
|
|
|
}
|
|
|
|
}
|
2016-08-24 23:48:25 +02:00
|
|
|
else if (requestCode == ADD_KEYINFO) {
|
|
|
|
if (resultCode == RESULT_OK) {
|
2016-11-13 18:21:00 +01:00
|
|
|
final KeyProfile keyProfile = (KeyProfile) data.getSerializableExtra("KeyProfile");
|
2016-08-24 23:48:25 +02:00
|
|
|
|
|
|
|
String otp;
|
|
|
|
try {
|
|
|
|
otp = OTP.generateOTP(keyProfile.Info);
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-10-25 23:53:33 +02:00
|
|
|
keyProfile.Order = mKeyProfiles.size() + 1;
|
2016-08-24 23:48:25 +02:00
|
|
|
keyProfile.Code = otp;
|
|
|
|
try {
|
|
|
|
database.addKey(keyProfile);
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
2016-11-13 18:21:00 +01:00
|
|
|
// TODO: feedback
|
|
|
|
return;
|
2016-08-24 23:48:25 +02:00
|
|
|
}
|
2016-11-13 18:21:00 +01:00
|
|
|
|
|
|
|
mKeyProfiles.add(keyProfile);
|
|
|
|
mKeyProfileAdapter.notifyDataSetChanged();
|
|
|
|
saveDatabase(true, null);
|
2016-08-24 23:48:25 +02:00
|
|
|
}
|
|
|
|
}
|
2016-08-16 00:08:01 +02:00
|
|
|
}
|
2016-08-16 14:14:17 +02:00
|
|
|
|
2016-09-30 01:08:03 +02:00
|
|
|
@Override
|
|
|
|
protected void onResume() {
|
|
|
|
super.onResume();
|
2016-11-01 23:43:46 +01:00
|
|
|
mKeyProfileAdapter.notifyDataSetChanged();
|
2016-09-30 01:08:03 +02:00
|
|
|
setPreferredTheme();
|
|
|
|
}
|
|
|
|
|
2016-10-25 23:53:33 +02:00
|
|
|
@Override
|
|
|
|
protected void onPause() {
|
2016-11-13 18:21:00 +01:00
|
|
|
if (!blockSave) {
|
|
|
|
// update order of keys
|
|
|
|
for (int i = 0; i < mKeyProfiles.size(); i++) {
|
|
|
|
try {
|
|
|
|
database.updateKey(mKeyProfiles.get(i));
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
2016-10-25 23:53:33 +02:00
|
|
|
}
|
2016-11-13 18:21:00 +01:00
|
|
|
|
|
|
|
saveDatabase(false, null);
|
2016-10-25 23:53:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
super.onPause();
|
|
|
|
}
|
|
|
|
|
2016-11-13 18:21:00 +01:00
|
|
|
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(false, cipher);
|
|
|
|
break;
|
|
|
|
case LOAD:
|
|
|
|
loadDatabase(cipher);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void saveDatabase(boolean allowPrompt, Cipher cipher) {
|
|
|
|
try {
|
|
|
|
byte[] bytes = database.serialize();
|
|
|
|
CryptParameters cryptParams = null;
|
|
|
|
DerivationParameters derParams = null;
|
|
|
|
|
|
|
|
switch (databaseFile.getLevel()) {
|
|
|
|
case DatabaseFile.SEC_LEVEL_DERIVED:
|
|
|
|
// TODO
|
|
|
|
break;
|
|
|
|
case DatabaseFile.SEC_LEVEL_KEYSTORE:
|
|
|
|
if (cipher == null) {
|
|
|
|
KeyStoreHandle keyStore = new KeyStoreHandle();
|
|
|
|
SecretKey key = keyStore.getKey();
|
|
|
|
cipher = CryptoUtils.createCipher(key, Cipher.ENCRYPT_MODE);
|
|
|
|
}
|
|
|
|
|
|
|
|
CryptResult result = CryptoUtils.encrypt(bytes, cipher);
|
|
|
|
bytes = result.Data;
|
|
|
|
cryptParams = result.Parameters;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
databaseFile.setContent(bytes);
|
|
|
|
databaseFile.setCryptParameters(cryptParams);
|
|
|
|
databaseFile.setDerivationParameters(derParams);
|
|
|
|
databaseFile.save();
|
|
|
|
} catch (IllegalBlockSizeException e) {
|
|
|
|
// TODO: is there a way to catch "Key user not authenticated" specifically aside from checking the exception message?
|
|
|
|
if (causeIsKeyUserNotAuthenticated(e) && allowPrompt && cipher != null) {
|
|
|
|
promptFingerPrint(FingerprintAuthenticationDialogFragment.Action.SAVE, cipher);
|
|
|
|
}
|
|
|
|
e.printStackTrace();
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-01 22:16:54 +01:00
|
|
|
private BottomSheetDialog InitializeBottomSheet()
|
|
|
|
{
|
|
|
|
View bottomSheetView = getLayoutInflater ().inflate (R.layout.bottom_sheet_edit_profile, null);
|
|
|
|
LinearLayout copyLayout = (LinearLayout)bottomSheetView.findViewById(R.id.copy_button);
|
|
|
|
LinearLayout deleteLayout = (LinearLayout)bottomSheetView.findViewById(R.id.delete_button);
|
|
|
|
LinearLayout editLayout = (LinearLayout)bottomSheetView.findViewById(R.id.edit_button);
|
|
|
|
bottomSheetView.findViewById(R.id.edit_button);
|
|
|
|
BottomSheetDialog bottomDialog = new BottomSheetDialog(this);
|
|
|
|
bottomDialog.setContentView(bottomSheetView);
|
|
|
|
bottomDialog.setCancelable (true);
|
|
|
|
bottomDialog.getWindow ().setLayout (LinearLayout.LayoutParams.MATCH_PARENT,
|
|
|
|
LinearLayout.LayoutParams.WRAP_CONTENT);
|
|
|
|
bottomDialog.show();
|
|
|
|
|
|
|
|
copyLayout.setOnClickListener(view -> {
|
|
|
|
bottomDialog.dismiss();
|
|
|
|
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
|
|
|
ClipData clip = ClipData.newPlainText("text/plain", mKeyProfiles.get(clickedItemPosition).Code);
|
|
|
|
clipboard.setPrimaryClip(clip);
|
|
|
|
|
|
|
|
Toast.makeText(this.getApplicationContext(), "Code successfully copied to the clipboard", Toast.LENGTH_SHORT).show();
|
|
|
|
});
|
2016-11-01 22:57:21 +01:00
|
|
|
|
|
|
|
deleteLayout.setOnClickListener(view -> {
|
|
|
|
bottomDialog.dismiss();
|
|
|
|
|
|
|
|
KeyProfile keyProfile = mKeyProfiles.get(clickedItemPosition);
|
|
|
|
deleteProfile(keyProfile);
|
|
|
|
});
|
|
|
|
|
|
|
|
editLayout.setOnClickListener(view -> {
|
|
|
|
bottomDialog.dismiss();
|
|
|
|
Toast.makeText(this.getApplicationContext(), "Coming soon", Toast.LENGTH_SHORT).show();
|
|
|
|
});
|
|
|
|
|
2016-11-01 22:16:54 +01:00
|
|
|
return bottomDialog;
|
2016-11-01 22:57:21 +01:00
|
|
|
}
|
2016-11-01 22:16:54 +01:00
|
|
|
|
2016-11-01 22:57:21 +01:00
|
|
|
private void deleteProfile(KeyProfile profile)
|
|
|
|
{
|
|
|
|
new AlertDialog.Builder(MainActivity.this)
|
|
|
|
.setTitle("Delete entry")
|
|
|
|
.setMessage("Are you sure you want to delete this profile?")
|
|
|
|
.setPositiveButton(android.R.string.yes, (dialog, which) -> {
|
2016-11-13 18:21:00 +01:00
|
|
|
try {
|
|
|
|
database.removeKey(profile);
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
//TODO: feedback
|
|
|
|
return;
|
|
|
|
}
|
2016-11-01 22:57:21 +01:00
|
|
|
mKeyProfiles.remove(clickedItemPosition);
|
|
|
|
mKeyProfileAdapter.notifyItemRemoved(clickedItemPosition);
|
|
|
|
})
|
|
|
|
.setNegativeButton(android.R.string.no, (dialog, which) -> {
|
|
|
|
|
|
|
|
})
|
|
|
|
.show();
|
2016-11-01 22:16:54 +01:00
|
|
|
}
|
|
|
|
|
2016-08-16 14:14:17 +02:00
|
|
|
@Override
|
|
|
|
public boolean onCreateOptionsMenu(Menu menu) {
|
|
|
|
getMenuInflater().inflate(R.menu.menu_main, menu);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
|
|
int id = item.getItemId();
|
|
|
|
|
|
|
|
//noinspection SimplifiableIfStatement
|
|
|
|
if (id == R.id.action_settings) {
|
2016-09-30 01:07:56 +02:00
|
|
|
|
|
|
|
Intent preferencesActivity = new Intent(this, PreferencesActivity.class);
|
|
|
|
startActivity(preferencesActivity);
|
|
|
|
|
2016-08-16 14:14:17 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return super.onOptionsItemSelected(item);
|
|
|
|
}
|
2016-09-30 01:08:03 +02:00
|
|
|
|
2016-11-03 22:03:34 +01:00
|
|
|
private void initializeAppShortcuts()
|
|
|
|
{
|
2016-11-13 18:00:13 +01:00
|
|
|
String mode = getIntent().getStringExtra("Action");
|
|
|
|
if(mode != null)
|
|
|
|
{
|
|
|
|
Log.println(Log.DEBUG, "MODE: ", mode);
|
|
|
|
if(Objects.equals(mode, "Scan"))
|
|
|
|
{
|
|
|
|
Log.println(Log.DEBUG, "OKK ", "OKKK");
|
|
|
|
Intent scannerActivity = new Intent(getApplicationContext(), ScannerActivity.class);
|
|
|
|
startActivityForResult(scannerActivity, GET_KEYINFO);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-03 22:03:34 +01:00
|
|
|
ShortcutManager shortcutManager = null;
|
|
|
|
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
|
|
|
shortcutManager = getSystemService(ShortcutManager.class);
|
|
|
|
if(shortcutManager != null) {
|
2016-11-13 18:00:13 +01:00
|
|
|
//TODO: Remove this line
|
|
|
|
shortcutManager.removeAllDynamicShortcuts();
|
2016-11-03 22:03:34 +01:00
|
|
|
if (shortcutManager.getDynamicShortcuts().size() == 0) {
|
|
|
|
// Application restored. Need to re-publish dynamic shortcuts.
|
|
|
|
|
2016-11-13 18:00:13 +01:00
|
|
|
Intent intent1 = new Intent(this.getBaseContext(), MainActivity.class);
|
|
|
|
intent1.putExtra("Action", "Scan");
|
|
|
|
intent1.setAction(Intent.ACTION_MAIN);
|
2016-11-03 22:03:34 +01:00
|
|
|
|
|
|
|
ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "id1")
|
2016-11-13 18:00:13 +01:00
|
|
|
.setShortLabel("New profile")
|
2016-11-03 22:03:34 +01:00
|
|
|
.setLongLabel("Add new profile")
|
|
|
|
.setIcon(Icon.createWithResource(this.getApplicationContext(), R.drawable.intro_scanner))
|
2016-11-13 18:00:13 +01:00
|
|
|
.setIntent(intent1)
|
2016-11-03 22:03:34 +01:00
|
|
|
.build();
|
|
|
|
|
|
|
|
shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-30 01:08:03 +02:00
|
|
|
private void setPreferredTheme()
|
|
|
|
{
|
2016-10-26 00:07:39 +02:00
|
|
|
boolean restart = false;
|
2016-09-30 01:08:03 +02:00
|
|
|
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
2016-10-26 00:07:39 +02:00
|
|
|
if(sharedPreferences.getBoolean("pref_night_mode", false)) {
|
|
|
|
if(!nightMode) {
|
2016-09-30 01:08:03 +02:00
|
|
|
setTheme(R.style.AppTheme_Dark_NoActionBar);
|
2016-10-26 00:07:39 +02:00
|
|
|
restart = true;
|
2016-09-30 01:08:03 +02:00
|
|
|
}
|
2016-10-26 00:07:39 +02:00
|
|
|
} else {
|
|
|
|
if(nightMode) {
|
2016-09-30 01:08:03 +02:00
|
|
|
setTheme(R.style.AppTheme_Default_NoActionBar);
|
2016-10-26 00:07:39 +02:00
|
|
|
restart = true;
|
2016-09-30 01:08:03 +02:00
|
|
|
}
|
|
|
|
}
|
2016-10-26 00:07:39 +02:00
|
|
|
|
|
|
|
if(restart){
|
|
|
|
finish();
|
|
|
|
startActivity(new Intent(this, this.getClass()));
|
|
|
|
}
|
2016-09-30 01:08:03 +02:00
|
|
|
}
|
2016-11-13 18:21:00 +01:00
|
|
|
|
|
|
|
private void loadDatabase(Cipher cipher) {
|
|
|
|
try {
|
|
|
|
databaseFile = DatabaseFile.load(getApplicationContext());
|
|
|
|
} catch (IOException e) {
|
|
|
|
// this file doesn't exist yet
|
|
|
|
try {
|
|
|
|
// TODO: prompt for security settings (level, auth, etc)
|
|
|
|
database = new Database();
|
|
|
|
databaseFile = new DatabaseFile(getApplicationContext());
|
|
|
|
databaseFile.setLevel(DatabaseFile.SEC_LEVEL_KEYSTORE);
|
|
|
|
|
|
|
|
if (databaseFile.getLevel() == DatabaseFile.SEC_LEVEL_KEYSTORE) {
|
|
|
|
KeyStoreHandle store = new KeyStoreHandle();
|
|
|
|
if (!store.keyExists()) {
|
|
|
|
store.generateKey(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (Exception ex) {
|
|
|
|
e.printStackTrace();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
|
|
// something else went wrong
|
|
|
|
e.printStackTrace();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (database == null) {
|
|
|
|
byte[] content = databaseFile.getContent();
|
|
|
|
switch (databaseFile.getLevel()) {
|
|
|
|
case DatabaseFile.SEC_LEVEL_NONE:
|
|
|
|
try {
|
|
|
|
Database temp = new Database();
|
|
|
|
temp.deserialize(content);
|
|
|
|
database = temp;
|
|
|
|
} catch (Exception e) {
|
|
|
|
// TODO: handle corrupt database
|
|
|
|
e.printStackTrace();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DatabaseFile.SEC_LEVEL_DERIVED:
|
|
|
|
// TODO: prompt for pin/pass
|
|
|
|
/*CryptParameters cryptParams = dbFile.getCryptParameters();
|
|
|
|
DerivationParameters derParams = dbFile.getDerivationParameters();
|
|
|
|
SecretKey key = CryptoUtils.deriveKey("password".toCharArray(), derParams.Salt, (int)derParams.IterationCount);*/
|
|
|
|
|
|
|
|
break;
|
|
|
|
case DatabaseFile.SEC_LEVEL_KEYSTORE:
|
|
|
|
// TODO: prompt for fingerprint if auth is required
|
|
|
|
try {
|
|
|
|
CryptParameters params = databaseFile.getCryptParameters();
|
|
|
|
|
|
|
|
if (cipher == null) {
|
|
|
|
KeyStoreHandle store = new KeyStoreHandle();
|
|
|
|
SecretKey key = store.getKey();
|
|
|
|
cipher = CryptoUtils.createCipher(key, Cipher.DECRYPT_MODE, params.Nonce);
|
|
|
|
}
|
|
|
|
|
|
|
|
CryptResult result = null;
|
|
|
|
//try {
|
|
|
|
result = CryptoUtils.decrypt(content, cipher, params);
|
|
|
|
//} catch (Exception e) {
|
|
|
|
// // we probably need to authenticate ourselves
|
|
|
|
// promptFingerPrint(1, cipher);
|
|
|
|
//}
|
|
|
|
if (result != null) {
|
|
|
|
database = new Database();
|
|
|
|
database.deserialize(result.Data);
|
|
|
|
}
|
|
|
|
} catch (IllegalBlockSizeException e) {
|
|
|
|
if (causeIsKeyUserNotAuthenticated(e) && cipher != null) {
|
|
|
|
promptFingerPrint(FingerprintAuthenticationDialogFragment.Action.LOAD, cipher);
|
|
|
|
}
|
|
|
|
e.printStackTrace();
|
|
|
|
return;
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// TODO: handle unknown security level
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
mKeyProfiles.addAll(database.getKeys());
|
|
|
|
mKeyProfileAdapter.notifyDataSetChanged();
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
|
|
|
|
fab.setEnabled(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean causeIsKeyUserNotAuthenticated(Exception e) {
|
|
|
|
// TODO: is there a way to catch "Key user not authenticated" specifically aside from checking the exception message?
|
|
|
|
return e.getCause().getMessage().equals("Key user not authenticated");
|
|
|
|
}
|
2016-08-15 21:29:41 +02:00
|
|
|
}
|