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;
|
2017-12-10 19:19:48 +01:00
|
|
|
import android.content.DialogInterface;
|
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;
|
2017-12-10 19:19:48 +01:00
|
|
|
import android.media.MediaScannerConnection;
|
2016-11-03 22:03:34 +01:00
|
|
|
import android.os.Build;
|
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-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;
|
|
|
|
|
2017-12-03 21:42:12 +01:00
|
|
|
import java.io.ByteArrayOutputStream;
|
2017-11-26 19:27:03 +01:00
|
|
|
import java.io.FileNotFoundException;
|
2017-08-26 15:47:57 +02:00
|
|
|
import java.io.InputStream;
|
2017-05-03 21:08:38 +02:00
|
|
|
import java.lang.reflect.UndeclaredThrowableException;
|
2016-08-16 20:04:38 +02:00
|
|
|
import java.util.ArrayList;
|
2016-10-25 23:53:33 +02:00
|
|
|
import java.util.Collections;
|
2017-12-03 21:42:12 +01:00
|
|
|
import java.util.List;
|
2016-08-16 20:04:38 +02:00
|
|
|
|
2017-05-03 21:08:38 +02:00
|
|
|
import me.impy.aegis.crypto.MasterKey;
|
2017-08-26 21:15:53 +02:00
|
|
|
import me.impy.aegis.db.DatabaseEntry;
|
2017-08-06 16:03:36 +02:00
|
|
|
import me.impy.aegis.db.DatabaseManager;
|
2017-12-04 21:21:31 +01:00
|
|
|
import me.impy.aegis.importers.DatabaseImporter;
|
2016-10-04 22:23:34 +02:00
|
|
|
import me.impy.aegis.helpers.SimpleItemTouchHelperCallback;
|
2017-12-03 21:42:12 +01:00
|
|
|
import me.impy.aegis.util.ByteInputStream;
|
2016-08-15 21:29:41 +02:00
|
|
|
|
2017-12-12 01:50:00 +01:00
|
|
|
public class MainActivity extends AppCompatActivity implements KeyProfileAdapter.Listener {
|
2017-08-06 16:03:36 +02:00
|
|
|
private static final int CODE_GET_KEYINFO = 0;
|
|
|
|
private static final int CODE_ADD_KEYINFO = 1;
|
|
|
|
private static final int CODE_DO_INTRO = 2;
|
2017-08-06 18:15:47 +02:00
|
|
|
private static final int CODE_DECRYPT = 3;
|
2017-08-26 15:47:57 +02:00
|
|
|
private static final int CODE_IMPORT = 4;
|
2017-12-10 19:19:48 +01:00
|
|
|
private static final int CODE_PREFERENCES = 5;
|
2016-08-24 23:48:25 +02:00
|
|
|
|
2017-08-26 21:15:53 +02:00
|
|
|
private KeyProfileAdapter _keyProfileAdapter;
|
|
|
|
private DatabaseManager _db;
|
2016-09-30 01:08:03 +02:00
|
|
|
|
2017-08-26 21:15:53 +02:00
|
|
|
private boolean _nightMode = false;
|
|
|
|
private Menu _menu;
|
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);
|
2017-08-26 21:15:53 +02:00
|
|
|
_db = new DatabaseManager(getApplicationContext());
|
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);
|
2017-08-06 16:03:36 +02:00
|
|
|
startActivityForResult(intro, CODE_DO_INTRO);
|
2017-08-06 18:15:47 +02:00
|
|
|
} else {
|
|
|
|
try {
|
2017-08-26 21:15:53 +02:00
|
|
|
_db.load();
|
2017-11-26 19:27:03 +01:00
|
|
|
if (!_db.isDecrypted()) {
|
|
|
|
Intent intent = new Intent(this, AuthActivity.class);
|
|
|
|
intent.putExtra("slots", _db.getFile().getSlots());
|
|
|
|
startActivityForResult(intent, CODE_DECRYPT);
|
|
|
|
}
|
|
|
|
} catch (FileNotFoundException e) {
|
|
|
|
// start the intro if the db file was not found
|
|
|
|
Toast.makeText(this, "Database file not found, starting over...", Toast.LENGTH_SHORT).show();
|
|
|
|
Intent intro = new Intent(this, IntroActivity.class);
|
|
|
|
startActivityForResult(intro, CODE_DO_INTRO);
|
2017-08-06 18:15:47 +02:00
|
|
|
} catch (Exception e) {
|
2017-11-27 21:06:23 +01:00
|
|
|
e.printStackTrace();
|
|
|
|
Toast.makeText(this, "An error occurred while trying to deserialize the database", Toast.LENGTH_LONG).show();
|
2017-08-06 18:15:47 +02:00
|
|
|
throw new UndeclaredThrowableException(e);
|
|
|
|
}
|
2016-09-29 12:31:55 +02:00
|
|
|
}
|
|
|
|
|
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)) {
|
2017-08-26 21:15:53 +02:00
|
|
|
_nightMode = true;
|
2016-09-30 01:08:03 +02:00
|
|
|
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);
|
2017-12-12 02:21:13 +01:00
|
|
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
2016-08-16 14:14:17 +02:00
|
|
|
setSupportActionBar(toolbar);
|
2017-12-03 16:47:27 +01:00
|
|
|
|
|
|
|
// init the app shortcuts and execute any pending actions
|
2016-11-03 22:03:34 +01:00
|
|
|
initializeAppShortcuts();
|
2017-12-03 16:47:27 +01:00
|
|
|
doShortcutActions();
|
|
|
|
|
2017-12-12 02:21:13 +01:00
|
|
|
FloatingActionButton fab = findViewById(R.id.fab);
|
2017-05-03 21:08:38 +02:00
|
|
|
fab.setEnabled(true);
|
2016-10-26 00:07:39 +02:00
|
|
|
fab.setOnClickListener(view -> {
|
|
|
|
Intent scannerActivity = new Intent(getApplicationContext(), ScannerActivity.class);
|
2017-08-06 16:03:36 +02:00
|
|
|
startActivityForResult(scannerActivity, CODE_GET_KEYINFO);
|
2016-08-15 22:31:28 +02:00
|
|
|
});
|
2016-08-16 14:14:17 +02:00
|
|
|
|
2017-12-12 02:21:13 +01:00
|
|
|
RecyclerView rvKeyProfiles = findViewById(R.id.rvKeyProfiles);
|
2016-08-16 20:04:38 +02:00
|
|
|
LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
|
|
|
|
rvKeyProfiles.setLayoutManager(mLayoutManager);
|
|
|
|
|
2017-12-12 02:19:29 +01:00
|
|
|
_keyProfileAdapter = new KeyProfileAdapter(this);
|
2017-12-11 14:01:43 +01:00
|
|
|
if (_db.isDecrypted()) {
|
|
|
|
loadKeyProfiles();
|
|
|
|
}
|
2016-10-28 13:18:11 +02:00
|
|
|
|
2017-08-26 21:15:53 +02:00
|
|
|
ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(_keyProfileAdapter);
|
2016-10-04 22:23:34 +02:00
|
|
|
ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
|
|
|
|
touchHelper.attachToRecyclerView(rvKeyProfiles);
|
2017-08-26 21:15:53 +02:00
|
|
|
rvKeyProfiles.setAdapter(_keyProfileAdapter);
|
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) {
|
2017-08-06 16:03:36 +02:00
|
|
|
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;
|
2017-08-06 18:15:47 +02:00
|
|
|
case CODE_DECRYPT:
|
|
|
|
onDecryptResult(resultCode, data);
|
|
|
|
break;
|
2017-08-26 15:47:57 +02:00
|
|
|
case CODE_IMPORT:
|
|
|
|
onImportResult(resultCode, data);
|
|
|
|
break;
|
2017-12-10 19:19:48 +01:00
|
|
|
case CODE_PREFERENCES:
|
|
|
|
onPreferencesResult(resultCode, data);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void onPreferencesResult(int resultCode, Intent data) {
|
|
|
|
if (resultCode != RESULT_OK) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: create a custom layout to show a message AND a checkbox
|
|
|
|
int action = data.getIntExtra("action", -1);
|
|
|
|
switch (action) {
|
|
|
|
case PreferencesActivity.ACTION_EXPORT:
|
|
|
|
final boolean[] checked = {true};
|
|
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this)
|
|
|
|
.setTitle("Export the database")
|
|
|
|
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
|
|
|
String filename;
|
|
|
|
try {
|
|
|
|
filename = _db.export(checked[0]);
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
Toast.makeText(this, "An error occurred while trying to export the database", Toast.LENGTH_SHORT).show();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// make sure the new file is visible
|
|
|
|
MediaScannerConnection.scanFile(this, new String[]{filename}, null, null);
|
|
|
|
|
|
|
|
Toast.makeText(this, "The database has been exported to: " + filename, Toast.LENGTH_SHORT).show();
|
|
|
|
})
|
|
|
|
.setNegativeButton(android.R.string.cancel, null);
|
|
|
|
if (_db.getFile().isEncrypted()) {
|
|
|
|
final String[] items = {"Keep the database encrypted"};
|
|
|
|
final boolean[] checkedItems = {true};
|
|
|
|
builder.setMultiChoiceItems(items, checkedItems, new DialogInterface.OnMultiChoiceClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(DialogInterface dialog, int index, boolean isChecked) {
|
|
|
|
checked[0] = isChecked;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
builder.setMessage("This action will export the database out of Android's private storage.");
|
|
|
|
}
|
|
|
|
builder.show();
|
|
|
|
break;
|
2017-08-06 16:03:36 +02:00
|
|
|
}
|
|
|
|
}
|
2016-08-16 00:08:01 +02:00
|
|
|
|
2017-08-26 15:47:57 +02:00
|
|
|
private void onImportResult(int resultCode, Intent data) {
|
2017-08-26 21:15:53 +02:00
|
|
|
if (resultCode != RESULT_OK) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-03 21:42:12 +01:00
|
|
|
InputStream fileStream = null;
|
2017-08-26 15:47:57 +02:00
|
|
|
try {
|
|
|
|
try {
|
2017-12-03 21:42:12 +01:00
|
|
|
fileStream = getContentResolver().openInputStream(data.getData());
|
2017-08-26 15:47:57 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
Toast.makeText(this, "An error occurred while trying to open the file", Toast.LENGTH_SHORT).show();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-03 21:42:12 +01:00
|
|
|
ByteInputStream stream;
|
2017-08-26 15:47:57 +02:00
|
|
|
try {
|
2017-12-03 21:42:12 +01:00
|
|
|
int read;
|
|
|
|
byte[] buf = new byte[4096];
|
|
|
|
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
|
|
|
while ((read = fileStream.read(buf, 0, buf.length)) != -1) {
|
|
|
|
outStream.write(buf, 0, read);
|
2017-08-26 15:47:57 +02:00
|
|
|
}
|
2017-12-03 21:42:12 +01:00
|
|
|
stream = new ByteInputStream(outStream.toByteArray());
|
2017-08-26 15:47:57 +02:00
|
|
|
} catch (Exception e) {
|
2017-12-03 21:42:12 +01:00
|
|
|
Toast.makeText(this, "An error occurred while trying to read the file", Toast.LENGTH_SHORT).show();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
List<DatabaseEntry> entries = null;
|
|
|
|
for (DatabaseImporter converter : DatabaseImporter.create(stream)) {
|
|
|
|
try {
|
|
|
|
entries = converter.convert();
|
|
|
|
break;
|
|
|
|
} catch (Exception e) {
|
|
|
|
stream.reset();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entries == null) {
|
2017-08-26 15:47:57 +02:00
|
|
|
Toast.makeText(this, "An error occurred while trying to parse the file", Toast.LENGTH_SHORT).show();
|
|
|
|
return;
|
|
|
|
}
|
2017-12-03 21:42:12 +01:00
|
|
|
|
|
|
|
for (DatabaseEntry entry : entries) {
|
|
|
|
addKey(new KeyProfile(entry));
|
|
|
|
}
|
2017-08-26 15:47:57 +02:00
|
|
|
} finally {
|
2017-12-03 21:42:12 +01:00
|
|
|
if (fileStream != null) {
|
2017-08-26 21:15:53 +02:00
|
|
|
try {
|
2017-12-03 21:42:12 +01:00
|
|
|
fileStream.close();
|
2017-08-26 21:15:53 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
}
|
2017-08-26 15:47:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
saveDatabase();
|
|
|
|
}
|
|
|
|
|
2017-08-06 16:03:36 +02:00
|
|
|
private void onGetKeyInfoResult(int resultCode, Intent data) {
|
2017-12-03 16:47:27 +01:00
|
|
|
if (resultCode == RESULT_OK) {
|
|
|
|
KeyProfile keyProfile = (KeyProfile)data.getSerializableExtra("KeyProfile");
|
|
|
|
Intent intent = new Intent(this, AddProfileActivity.class);
|
|
|
|
intent.putExtra("KeyProfile", keyProfile);
|
|
|
|
startActivityForResult(intent, CODE_ADD_KEYINFO);
|
2016-08-16 00:08:01 +02:00
|
|
|
}
|
2017-08-06 16:03:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void onAddKeyInfoResult(int resultCode, Intent data) {
|
2017-12-03 16:47:27 +01:00
|
|
|
if (resultCode == RESULT_OK) {
|
|
|
|
KeyProfile profile = (KeyProfile) data.getSerializableExtra("KeyProfile");
|
|
|
|
addKey(profile);
|
|
|
|
saveDatabase();
|
2017-08-06 16:03:36 +02:00
|
|
|
}
|
2017-08-26 15:47:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void addKey(KeyProfile profile) {
|
2017-11-26 19:50:05 +01:00
|
|
|
profile.refreshCode();
|
2017-08-06 16:03:36 +02:00
|
|
|
|
2017-11-26 19:50:05 +01:00
|
|
|
DatabaseEntry entry = profile.getEntry();
|
2017-08-26 21:15:53 +02:00
|
|
|
entry.setName(entry.getInfo().getAccountName());
|
2017-08-06 16:03:36 +02:00
|
|
|
try {
|
2017-08-26 21:15:53 +02:00
|
|
|
_db.addKey(entry);
|
2017-08-06 16:03:36 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
2017-11-27 21:06:23 +01:00
|
|
|
Toast.makeText(this, "An error occurred while trying to add an entry", Toast.LENGTH_SHORT).show();
|
2017-08-06 16:03:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-12 02:19:29 +01:00
|
|
|
_keyProfileAdapter.addKey(profile);
|
2017-08-06 16:03:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2016-11-13 18:21:00 +01:00
|
|
|
|
2017-08-06 16:03:36 +02:00
|
|
|
MasterKey key = (MasterKey) data.getSerializableExtra("key");
|
|
|
|
try {
|
2017-08-26 21:15:53 +02:00
|
|
|
_db.load();
|
|
|
|
if (!_db.isDecrypted()) {
|
|
|
|
_db.setMasterKey(key);
|
2016-08-24 23:48:25 +02:00
|
|
|
}
|
2017-08-06 16:03:36 +02:00
|
|
|
} catch (Exception e) {
|
2017-11-27 21:06:23 +01:00
|
|
|
e.printStackTrace();
|
|
|
|
Toast.makeText(this, "An error occurred while trying to load/decrypt the database", Toast.LENGTH_LONG).show();
|
|
|
|
recreate();
|
|
|
|
return;
|
2016-08-24 23:48:25 +02:00
|
|
|
}
|
2017-08-06 18:15:47 +02:00
|
|
|
|
|
|
|
loadKeyProfiles();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void onDecryptResult(int resultCode, Intent data) {
|
|
|
|
MasterKey key = (MasterKey) data.getSerializableExtra("key");
|
|
|
|
try {
|
2017-08-26 21:15:53 +02:00
|
|
|
_db.setMasterKey(key);
|
2017-08-06 18:15:47 +02:00
|
|
|
} catch (Exception e) {
|
2017-11-27 21:06:23 +01:00
|
|
|
e.printStackTrace();
|
|
|
|
Toast.makeText(this, "An error occurred while trying to decrypt the database", Toast.LENGTH_LONG).show();
|
|
|
|
recreate();
|
|
|
|
return;
|
2017-08-06 18:15:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
loadKeyProfiles();
|
2017-12-03 16:47:27 +01:00
|
|
|
doShortcutActions();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void doShortcutActions() {
|
|
|
|
Intent intent = getIntent();
|
|
|
|
String mode = intent.getStringExtra("Action");
|
|
|
|
if (mode == null || !_db.isDecrypted()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case "Scan":
|
|
|
|
Intent scannerActivity = new Intent(getApplicationContext(), ScannerActivity.class);
|
|
|
|
startActivityForResult(scannerActivity, CODE_GET_KEYINFO);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
intent.removeExtra("Action");
|
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();
|
|
|
|
setPreferredTheme();
|
|
|
|
}
|
|
|
|
|
2016-10-25 23:53:33 +02:00
|
|
|
@Override
|
2017-12-03 22:00:46 +01:00
|
|
|
protected void onStop() {
|
2017-05-03 21:08:38 +02:00
|
|
|
saveDatabase();
|
2017-12-03 22:00:46 +01:00
|
|
|
super.onStop();
|
2016-10-25 23:53:33 +02:00
|
|
|
}
|
|
|
|
|
2017-12-12 01:50:00 +01:00
|
|
|
private BottomSheetDialog createBottomSheet(KeyProfile profile) {
|
2017-11-27 21:38:02 +01:00
|
|
|
View bottomSheetView = getLayoutInflater().inflate(R.layout.bottom_sheet_edit_profile, null);
|
2017-12-12 02:21:13 +01:00
|
|
|
LinearLayout copyLayout = bottomSheetView.findViewById(R.id.copy_button);
|
|
|
|
LinearLayout deleteLayout = bottomSheetView.findViewById(R.id.delete_button);
|
|
|
|
LinearLayout editLayout = bottomSheetView.findViewById(R.id.edit_button);
|
2016-11-01 22:16:54 +01:00
|
|
|
bottomSheetView.findViewById(R.id.edit_button);
|
2017-11-27 21:38:02 +01:00
|
|
|
BottomSheetDialog bottomDialog = new BottomSheetDialog(this);
|
2016-11-01 22:16:54 +01:00
|
|
|
bottomDialog.setContentView(bottomSheetView);
|
2017-11-27 21:38:02 +01:00
|
|
|
bottomDialog.setCancelable(true);
|
|
|
|
bottomDialog.getWindow().setLayout(LinearLayout.LayoutParams.MATCH_PARENT,
|
2016-11-01 22:16:54 +01:00
|
|
|
LinearLayout.LayoutParams.WRAP_CONTENT);
|
|
|
|
bottomDialog.show();
|
|
|
|
|
|
|
|
copyLayout.setOnClickListener(view -> {
|
|
|
|
bottomDialog.dismiss();
|
|
|
|
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
2017-12-12 01:50:00 +01:00
|
|
|
ClipData clip = ClipData.newPlainText("text/plain", profile.getCode());
|
2016-11-01 22:16:54 +01:00
|
|
|
clipboard.setPrimaryClip(clip);
|
2017-11-27 21:38:02 +01:00
|
|
|
Toast.makeText(this.getApplicationContext(), "Code copied to the clipboard", Toast.LENGTH_SHORT).show();
|
2016-11-01 22:16:54 +01:00
|
|
|
});
|
2016-11-01 22:57:21 +01:00
|
|
|
|
|
|
|
deleteLayout.setOnClickListener(view -> {
|
|
|
|
bottomDialog.dismiss();
|
2017-12-12 01:50:00 +01:00
|
|
|
deleteProfile(profile);
|
2016-11-01 22:57:21 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
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
|
|
|
|
2017-12-12 01:50:00 +01:00
|
|
|
private void deleteProfile(KeyProfile profile) {
|
2016-11-01 22:57:21 +01:00
|
|
|
new AlertDialog.Builder(MainActivity.this)
|
2017-05-03 21:08:38 +02:00
|
|
|
.setTitle("Delete entry")
|
|
|
|
.setMessage("Are you sure you want to delete this profile?")
|
|
|
|
.setPositiveButton(android.R.string.yes, (dialog, which) -> {
|
|
|
|
try {
|
2017-08-26 21:15:53 +02:00
|
|
|
_db.removeKey(profile.getEntry());
|
2017-05-03 21:08:38 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
2017-11-27 21:06:23 +01:00
|
|
|
Toast.makeText(this, "An error occurred while trying to delete an entry", Toast.LENGTH_SHORT).show();
|
2017-05-03 21:08:38 +02:00
|
|
|
return;
|
|
|
|
}
|
2017-12-12 02:19:29 +01:00
|
|
|
_keyProfileAdapter.removeKey(profile);
|
2017-05-03 21:08:38 +02:00
|
|
|
})
|
2017-11-27 21:06:23 +01:00
|
|
|
.setNegativeButton(android.R.string.no, null)
|
2017-05-03 21:08:38 +02:00
|
|
|
.show();
|
2016-11-01 22:16:54 +01:00
|
|
|
}
|
|
|
|
|
2016-08-16 14:14:17 +02:00
|
|
|
@Override
|
|
|
|
public boolean onCreateOptionsMenu(Menu menu) {
|
2017-08-26 21:15:53 +02:00
|
|
|
_menu = menu;
|
2016-08-16 14:14:17 +02:00
|
|
|
getMenuInflater().inflate(R.menu.menu_main, menu);
|
2017-08-26 21:15:53 +02:00
|
|
|
updateLockIcon();
|
2016-08-16 14:14:17 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
2017-08-14 00:04:06 +02:00
|
|
|
switch (item.getItemId()) {
|
|
|
|
case R.id.action_settings:
|
|
|
|
Intent preferencesActivity = new Intent(this, PreferencesActivity.class);
|
2017-12-10 19:19:48 +01:00
|
|
|
startActivityForResult(preferencesActivity, CODE_PREFERENCES);
|
2017-08-14 00:04:06 +02:00
|
|
|
return true;
|
2017-08-26 15:47:57 +02:00
|
|
|
case R.id.action_import:
|
|
|
|
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
|
|
|
intent.setType("*/*");
|
|
|
|
startActivityForResult(intent, CODE_IMPORT);
|
|
|
|
return true;
|
2017-08-14 00:04:06 +02:00
|
|
|
case R.id.action_lock:
|
|
|
|
// TODO: properly close the database
|
|
|
|
recreate();
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return super.onOptionsItemSelected(item);
|
2016-08-16 14:14:17 +02:00
|
|
|
}
|
|
|
|
}
|
2016-09-30 01:08:03 +02:00
|
|
|
|
2017-08-26 21:15:53 +02:00
|
|
|
private void initializeAppShortcuts() {
|
2017-12-03 16:47:27 +01:00
|
|
|
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) {
|
|
|
|
return;
|
2016-11-13 18:00:13 +01:00
|
|
|
}
|
|
|
|
|
2017-12-03 16:47:27 +01:00
|
|
|
ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
|
|
|
|
if (shortcutManager == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Remove this line
|
|
|
|
shortcutManager.removeAllDynamicShortcuts();
|
|
|
|
if (shortcutManager.getDynamicShortcuts().size() == 0) {
|
|
|
|
// Application restored. Need to re-publish dynamic shortcuts.
|
|
|
|
Intent intent = new Intent(this.getBaseContext(), MainActivity.class);
|
|
|
|
intent.putExtra("Action", "Scan");
|
|
|
|
intent.setAction(Intent.ACTION_MAIN);
|
|
|
|
|
|
|
|
ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "id1")
|
|
|
|
.setShortLabel("New profile")
|
|
|
|
.setLongLabel("Add new profile")
|
|
|
|
.setIcon(Icon.createWithResource(this.getApplicationContext(), R.drawable.intro_scanner))
|
|
|
|
.setIntent(intent)
|
|
|
|
.build();
|
|
|
|
|
|
|
|
shortcutManager.setDynamicShortcuts(Collections.singletonList(shortcut));
|
2016-11-03 22:03:34 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-26 21:15:53 +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);
|
2017-08-26 21:15:53 +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
|
|
|
}
|
2017-12-03 16:47:27 +01:00
|
|
|
} else if (_nightMode) {
|
|
|
|
setTheme(R.style.AppTheme_Default_NoActionBar);
|
|
|
|
restart = true;
|
2016-09-30 01:08:03 +02:00
|
|
|
}
|
2016-10-26 00:07:39 +02:00
|
|
|
|
2017-08-26 21:15:53 +02:00
|
|
|
if (restart) {
|
2016-10-26 00:07:39 +02:00
|
|
|
finish();
|
|
|
|
startActivity(new Intent(this, this.getClass()));
|
|
|
|
}
|
2016-09-30 01:08:03 +02:00
|
|
|
}
|
2016-11-13 18:21:00 +01:00
|
|
|
|
2017-08-06 18:15:47 +02:00
|
|
|
private void saveDatabase() {
|
2017-08-26 21:15:53 +02:00
|
|
|
if (!_db.isDecrypted()) {
|
2017-05-03 21:08:38 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2017-08-26 21:15:53 +02:00
|
|
|
_db.save();
|
2017-05-03 21:08:38 +02:00
|
|
|
} catch (Exception e) {
|
2017-11-27 21:06:23 +01:00
|
|
|
e.printStackTrace();
|
|
|
|
Toast.makeText(this, "An error occurred while trying to save the database", Toast.LENGTH_LONG).show();
|
2016-11-13 18:21:00 +01:00
|
|
|
}
|
2017-08-06 18:15:47 +02:00
|
|
|
}
|
2016-11-13 18:21:00 +01:00
|
|
|
|
2017-08-06 18:15:47 +02:00
|
|
|
private void loadKeyProfiles() {
|
2017-08-26 21:15:53 +02:00
|
|
|
updateLockIcon();
|
|
|
|
|
2017-12-12 02:19:29 +01:00
|
|
|
List<KeyProfile> profiles = new ArrayList<>();
|
2016-11-13 18:21:00 +01:00
|
|
|
try {
|
2017-08-26 21:15:53 +02:00
|
|
|
for (DatabaseEntry entry : _db.getKeys()) {
|
2017-12-12 02:19:29 +01:00
|
|
|
profiles.add(new KeyProfile(entry));
|
2017-08-26 21:15:53 +02:00
|
|
|
}
|
2016-11-13 18:21:00 +01:00
|
|
|
} catch (Exception e) {
|
2017-08-06 18:15:47 +02:00
|
|
|
e.printStackTrace();
|
2017-11-27 21:06:23 +01:00
|
|
|
Toast.makeText(this, "An error occurred while trying to load database entries", Toast.LENGTH_SHORT).show();
|
|
|
|
return;
|
2017-08-06 16:03:36 +02:00
|
|
|
}
|
2017-11-26 22:10:10 +01:00
|
|
|
|
2017-12-12 02:19:29 +01:00
|
|
|
_keyProfileAdapter.addKeys(profiles);
|
2016-11-13 18:21:00 +01:00
|
|
|
}
|
2017-08-26 21:15:53 +02:00
|
|
|
|
|
|
|
private void updateLockIcon() {
|
|
|
|
// hide the lock icon if the database is not encrypted
|
|
|
|
if (_menu != null && _db.isDecrypted()) {
|
|
|
|
MenuItem item = _menu.findItem(R.id.action_lock);
|
|
|
|
item.setVisible(_db.getFile().isEncrypted());
|
|
|
|
}
|
|
|
|
}
|
2017-12-12 01:50:00 +01:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onKeyProfileClick(KeyProfile profile) {
|
|
|
|
createBottomSheet(profile).show();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onLongKeyProfileClick(KeyProfile profile) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onKeyProfileMove(KeyProfile profile1, KeyProfile profile2) {
|
|
|
|
try {
|
|
|
|
_db.swapKeys(profile1.getEntry(), profile2.getEntry());
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
throw new UndeclaredThrowableException(e);
|
|
|
|
}
|
|
|
|
}
|
2016-08-15 21:29:41 +02:00
|
|
|
}
|