diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2147dc33..cf09287e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,6 +8,7 @@ implements ItemTouchHelperAdapter { private ArrayList _keyProfiles; private static Listener _listener; + private boolean _showIssuer; - public KeyProfileAdapter(Listener listener) { + public KeyProfileAdapter(Listener listener, boolean showIssuer) { _keyProfiles = new ArrayList<>(); _listener = listener; + _showIssuer = showIssuer; + } + + public void setShowIssuer(boolean showIssuer) { + _showIssuer = showIssuer; } public void addKey(KeyProfile profile) { @@ -36,6 +42,11 @@ public class KeyProfileAdapter extends RecyclerView.Adapter im notifyItemRemoved(position); } + public void clearKeys() { + _keyProfiles.clear(); + notifyDataSetChanged(); + } + @Override public void onItemDismiss(int position) { @@ -64,14 +75,14 @@ public class KeyProfileAdapter extends RecyclerView.Adapter im @Override public void onViewRecycled(KeyProfileHolder holder) { - holder.setData(null); + holder.setData(null, _showIssuer); super.onViewRecycled(holder); } @Override public void onBindViewHolder(final KeyProfileHolder holder, int position) { final KeyProfile profile = _keyProfiles.get(position); - holder.setData(profile); + holder.setData(profile, _showIssuer); holder.startUpdateLoop(); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override diff --git a/app/src/main/java/me/impy/aegis/KeyProfileHolder.java b/app/src/main/java/me/impy/aegis/KeyProfileHolder.java index 7121037b..39aac446 100644 --- a/app/src/main/java/me/impy/aegis/KeyProfileHolder.java +++ b/app/src/main/java/me/impy/aegis/KeyProfileHolder.java @@ -1,9 +1,7 @@ package me.impy.aegis; import android.animation.ObjectAnimator; -import android.content.SharedPreferences; import android.os.Handler; -import android.preference.PreferenceManager; import android.support.v7.widget.RecyclerView; import android.view.View; import android.view.animation.LinearInterpolator; @@ -21,23 +19,21 @@ public class KeyProfileHolder extends RecyclerView.ViewHolder { private ImageView _profileDrawable; private KeyProfile _keyProfile; private ProgressBar _progressBar; - private View _itemView; private Handler _uiHandler; private boolean _running = false; - KeyProfileHolder(final View itemView) { - super(itemView); - _itemView = itemView; - _profileName = itemView.findViewById(R.id.profile_name); - _profileCode = itemView.findViewById(R.id.profile_code); - _profileIssuer = itemView.findViewById(R.id.profile_issuer); - _profileDrawable = itemView.findViewById(R.id.ivTextDrawable); - _progressBar = itemView.findViewById(R.id.progressBar); + KeyProfileHolder(final View view) { + super(view); + _profileName = view.findViewById(R.id.profile_name); + _profileCode = view.findViewById(R.id.profile_code); + _profileIssuer = view.findViewById(R.id.profile_issuer); + _profileDrawable = view.findViewById(R.id.ivTextDrawable); + _progressBar = view.findViewById(R.id.progressBar); _uiHandler = new Handler(); } - public void setData(KeyProfile profile) { + public void setData(KeyProfile profile, boolean showIssuer) { if ((_keyProfile = profile) == null) { _running = false; return; @@ -46,9 +42,7 @@ public class KeyProfileHolder extends RecyclerView.ViewHolder { _profileName.setText(profile.getEntry().getName()); _profileCode.setText(profile.getCode()); _profileIssuer.setText(""); - - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(_itemView.getContext()); - if (sharedPreferences.getBoolean("pref_issuer", false)) { + if (showIssuer) { _profileIssuer.setText(" - " + profile.getEntry().getInfo().getIssuer()); } diff --git a/app/src/main/java/me/impy/aegis/MainActivity.java b/app/src/main/java/me/impy/aegis/MainActivity.java index 3313eb26..79122daa 100644 --- a/app/src/main/java/me/impy/aegis/MainActivity.java +++ b/app/src/main/java/me/impy/aegis/MainActivity.java @@ -6,13 +6,11 @@ import android.content.ClipboardManager; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.SharedPreferences; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; import android.graphics.drawable.Icon; import android.media.MediaScannerConnection; import android.os.Build; -import android.preference.PreferenceManager; import android.support.design.widget.BottomSheetDialog; import android.support.design.widget.FloatingActionButton; import android.support.v7.app.AlertDialog; @@ -58,6 +56,7 @@ public class MainActivity extends AppCompatActivity implements KeyProfileAdapter private static final int CODE_PERM_CAMERA = 2; private KeyProfileAdapter _keyProfileAdapter; + private AegisApplication _app; private DatabaseManager _db; private boolean _nightMode = false; @@ -66,40 +65,18 @@ public class MainActivity extends AppCompatActivity implements KeyProfileAdapter @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - _db = new DatabaseManager(getApplicationContext()); + _app = (AegisApplication) getApplication(); + _db = _app.getDatabaseManager(); - SharedPreferences prefs = this.getSharedPreferences("me.impy.aegis", Context.MODE_PRIVATE); - if (!prefs.getBoolean("passedIntro", false)) { - Intent intro = new Intent(this, IntroActivity.class); - startActivityForResult(intro, CODE_DO_INTRO); - } else { - try { - _db.load(); - 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); - } catch (Exception e) { - e.printStackTrace(); - Toast.makeText(this, "An error occurred while trying to deserialize the database", Toast.LENGTH_LONG).show(); - throw new UndeclaredThrowableException(e); - } - } - - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - if (sharedPreferences.getBoolean("pref_night_mode", false)) { + // set the theme + if (_app.getPreferences().getBoolean("pref_night_mode", false)) { _nightMode = true; setTheme(R.style.AppTheme_Dark_NoActionBar); } else { setPreferredTheme(); } + // set up the main view setContentView(R.layout.activity_main); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); @@ -108,25 +85,49 @@ public class MainActivity extends AppCompatActivity implements KeyProfileAdapter initializeAppShortcuts(); doShortcutActions(); + // set up the floating action button FloatingActionButton fab = findViewById(R.id.fab); fab.setEnabled(true); - fab.setOnClickListener(view -> { - onGetKeyInfo(); - }); + fab.setOnClickListener(view -> onGetKeyInfo()); + // set up the recycler view for the key profiles + _keyProfileAdapter = new KeyProfileAdapter(this, _app.getPreferences().getBoolean("pref_issuer", false)); RecyclerView rvKeyProfiles = findViewById(R.id.rvKeyProfiles); LinearLayoutManager mLayoutManager = new LinearLayoutManager(this); rvKeyProfiles.setLayoutManager(mLayoutManager); - - _keyProfileAdapter = new KeyProfileAdapter(this); - if (_db.isDecrypted()) { - loadKeyProfiles(); - } - ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(_keyProfileAdapter); ItemTouchHelper touchHelper = new ItemTouchHelper(callback); touchHelper.attachToRecyclerView(rvKeyProfiles); rvKeyProfiles.setAdapter(_keyProfileAdapter); + + if (!_app.isRunning() && !_db.isUnlocked()) { + if (!_app.getPreferences().getBoolean("pref_intro", false)) { + Intent intro = new Intent(this, IntroActivity.class); + startActivityForResult(intro, CODE_DO_INTRO); + } else { + try { + _db.load(); + if (!_db.isUnlocked()) { + 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); + } catch (Exception e) { + e.printStackTrace(); + Toast.makeText(this, "An error occurred while trying to deserialize the database", Toast.LENGTH_LONG).show(); + throw new UndeclaredThrowableException(e); + } + } + } + + if (_db.isUnlocked()) { + loadKeyProfiles(); + } } @Override @@ -176,6 +177,8 @@ public class MainActivity extends AppCompatActivity implements KeyProfileAdapter private void onPreferencesResult(int resultCode, Intent data) { // refresh the entire key profile list if needed if (data.getBooleanExtra("needsRefresh", false)) { + boolean showIssuer = _app.getPreferences().getBoolean("pref_issuer", false); + _keyProfileAdapter.setShowIssuer(showIssuer); _keyProfileAdapter.notifyDataSetChanged(); } @@ -344,8 +347,8 @@ public class MainActivity extends AppCompatActivity implements KeyProfileAdapter MasterKey key = (MasterKey) data.getSerializableExtra("key"); try { _db.load(); - if (!_db.isDecrypted()) { - _db.setMasterKey(key); + if (!_db.isUnlocked()) { + _db.unlock(key); } } catch (Exception e) { e.printStackTrace(); @@ -360,7 +363,7 @@ public class MainActivity extends AppCompatActivity implements KeyProfileAdapter private void onDecryptResult(int resultCode, Intent data) { MasterKey key = (MasterKey) data.getSerializableExtra("key"); try { - _db.setMasterKey(key); + _db.unlock(key); } catch (Exception e) { e.printStackTrace(); Toast.makeText(this, "An error occurred while trying to decrypt the database", Toast.LENGTH_LONG).show(); @@ -375,7 +378,7 @@ public class MainActivity extends AppCompatActivity implements KeyProfileAdapter private void doShortcutActions() { Intent intent = getIntent(); String mode = intent.getStringExtra("Action"); - if (mode == null || !_db.isDecrypted()) { + if (mode == null || !_db.isUnlocked()) { return; } @@ -475,8 +478,16 @@ public class MainActivity extends AppCompatActivity implements KeyProfileAdapter } return true; case R.id.action_lock: - // TODO: properly close the database - recreate(); + _keyProfileAdapter.clearKeys(); + try { + _db.lock(); + } catch (Exception e) { + e.printStackTrace(); + Toast.makeText(this, "An error occurred while trying to lock the database", Toast.LENGTH_LONG).show(); + } + Intent intent = new Intent(this, AuthActivity.class); + intent.putExtra("slots", _db.getFile().getSlots()); + startActivityForResult(intent, CODE_DECRYPT); return true; default: return super.onOptionsItemSelected(item); @@ -514,8 +525,7 @@ public class MainActivity extends AppCompatActivity implements KeyProfileAdapter private void setPreferredTheme() { boolean restart = false; - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - if (sharedPreferences.getBoolean("pref_night_mode", false)) { + if (_app.getPreferences().getBoolean("pref_night_mode", false)) { if (!_nightMode) { setTheme(R.style.AppTheme_Dark_NoActionBar); restart = true; @@ -555,7 +565,7 @@ public class MainActivity extends AppCompatActivity implements KeyProfileAdapter private void updateLockIcon() { // hide the lock icon if the database is not encrypted - if (_menu != null && _db.isDecrypted()) { + if (_menu != null && _db.isUnlocked()) { MenuItem item = _menu.findItem(R.id.action_lock); item.setVisible(_db.getFile().isEncrypted()); } diff --git a/app/src/main/java/me/impy/aegis/PreferencesActivity.java b/app/src/main/java/me/impy/aegis/PreferencesActivity.java index fa7fde1e..6293e3f9 100644 --- a/app/src/main/java/me/impy/aegis/PreferencesActivity.java +++ b/app/src/main/java/me/impy/aegis/PreferencesActivity.java @@ -1,23 +1,23 @@ package me.impy.aegis; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Bundle; import android.preference.Preference; import android.preference.PreferenceFragment; -import android.preference.PreferenceManager; import android.support.v7.app.AppCompatActivity; import android.widget.Toast; public class PreferencesActivity extends AppCompatActivity { public static final int ACTION_EXPORT = 0; + private AegisApplication _app; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + _app = (AegisApplication) getApplication(); - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); - if (preferences.getBoolean("pref_night_mode", false)) { + if (_app.getPreferences().getBoolean("pref_night_mode", false)) { setTheme(R.style.AppTheme_Dark); } else { setTheme(R.style.AppTheme_Default); diff --git a/app/src/main/java/me/impy/aegis/db/DatabaseManager.java b/app/src/main/java/me/impy/aegis/db/DatabaseManager.java index b05c4fd6..85f7e7d0 100644 --- a/app/src/main/java/me/impy/aegis/db/DatabaseManager.java +++ b/app/src/main/java/me/impy/aegis/db/DatabaseManager.java @@ -57,7 +57,14 @@ public class DatabaseManager { } } - public void setMasterKey(MasterKey key) throws Exception { + public void lock() throws Exception { + assertUnlocked(); + // TODO: properly clear everything + _key = null; + _db = null; + } + + public void unlock(MasterKey key) throws Exception { assertLoaded(); byte[] encrypted = _file.getContent(); CryptParameters params = _file.getCryptParameters(); @@ -83,7 +90,7 @@ public class DatabaseManager { } public void save() throws Exception { - assertDecrypted(); + assertUnlocked(); byte[] dbBytes = _db.serialize(); if (!_file.isEncrypted()) { _file.setContent(dbBytes); @@ -96,7 +103,7 @@ public class DatabaseManager { } public String export(boolean encrypt) throws Exception { - assertDecrypted(); + assertUnlocked(); byte[] bytes = _db.serialize(); encrypt = encrypt && getFile().isEncrypted(); if (encrypt) { @@ -129,22 +136,22 @@ public class DatabaseManager { } public void addKey(DatabaseEntry entry) throws Exception { - assertDecrypted(); + assertUnlocked(); _db.addKey(entry); } public void removeKey(DatabaseEntry entry) throws Exception { - assertDecrypted(); + assertUnlocked(); _db.removeKey(entry); } public void swapKeys(DatabaseEntry entry1, DatabaseEntry entry2) throws Exception { - assertDecrypted(); + assertUnlocked(); _db.swapKeys(entry1, entry2); } public List getKeys() throws Exception { - assertDecrypted(); + assertUnlocked(); return _db.getKeys(); } @@ -156,7 +163,7 @@ public class DatabaseManager { return _file != null; } - public boolean isDecrypted() { + public boolean isUnlocked() { return _db != null; } @@ -166,10 +173,10 @@ public class DatabaseManager { } } - private void assertDecrypted() throws Exception { + private void assertUnlocked() throws Exception { assertLoaded(); - if (!isDecrypted()) { - throw new Exception("database file has not been decrypted yet"); + if (!isUnlocked()) { + throw new Exception("database file has not been unlocked yet"); } } }