mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-05-16 15:02:54 +00:00
Add an import button to the intro
This commit is contained in:
parent
dcda668671
commit
79022be3b6
13 changed files with 277 additions and 24 deletions
|
@ -3,6 +3,7 @@ package com.beemdevelopment.aegis.importers;
|
|||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
|
||||
import com.beemdevelopment.aegis.R;
|
||||
|
@ -72,7 +73,7 @@ public class AegisImporter extends DatabaseImporter {
|
|||
throw new DatabaseImporterException(e);
|
||||
}
|
||||
|
||||
return new DecryptedState(obj);
|
||||
return new DecryptedState(obj, creds);
|
||||
}
|
||||
|
||||
public State decrypt(char[] password) throws DatabaseImporterException {
|
||||
|
@ -109,10 +110,21 @@ public class AegisImporter extends DatabaseImporter {
|
|||
|
||||
public static class DecryptedState extends State {
|
||||
private JSONObject _obj;
|
||||
private VaultFileCredentials _creds;
|
||||
|
||||
private DecryptedState(JSONObject obj) {
|
||||
this(obj, null);
|
||||
}
|
||||
|
||||
private DecryptedState(JSONObject obj, VaultFileCredentials creds) {
|
||||
super(false);
|
||||
_obj = obj;
|
||||
_creds = creds;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public VaultFileCredentials getCredentials() {
|
||||
return _creds;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6,6 +6,7 @@ import static com.beemdevelopment.aegis.ui.slides.SecurityPickerSlide.CRYPT_TYPE
|
|||
import static com.beemdevelopment.aegis.ui.slides.SecurityPickerSlide.CRYPT_TYPE_PASS;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import com.beemdevelopment.aegis.R;
|
||||
|
@ -51,6 +52,18 @@ public class IntroActivity extends IntroBaseActivity {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (oldSlide == WelcomeSlide.class
|
||||
&& newSlide == SecurityPickerSlide.class
|
||||
&& getState().getBoolean("imported")) {
|
||||
skipToSlide(DoneSlide.class);
|
||||
return true;
|
||||
}
|
||||
|
||||
// on the welcome page, we don't want the keyboard to push any views up
|
||||
getWindow().setSoftInputMode(newSlide == WelcomeSlide.class
|
||||
? WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING
|
||||
: WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -58,21 +71,31 @@ public class IntroActivity extends IntroBaseActivity {
|
|||
protected void onDonePressed() {
|
||||
Bundle state = getState();
|
||||
|
||||
int cryptType = state.getInt("cryptType", CRYPT_TYPE_INVALID);
|
||||
VaultFileCredentials creds = (VaultFileCredentials) state.getSerializable("creds");
|
||||
if (cryptType == CRYPT_TYPE_INVALID
|
||||
|| (cryptType == CRYPT_TYPE_NONE && creds != null)
|
||||
|| (cryptType == CRYPT_TYPE_PASS && (creds == null || !creds.getSlots().has(PasswordSlot.class)))
|
||||
|| (cryptType == CRYPT_TYPE_BIOMETRIC && (creds == null || !creds.getSlots().has(PasswordSlot.class) || !creds.getSlots().has(BiometricSlot.class)))) {
|
||||
throw new RuntimeException(String.format("State of SecuritySetupSlide not properly propagated, cryptType: %d, creds: %s", cryptType, creds));
|
||||
}
|
||||
if (!state.getBoolean("imported")) {
|
||||
int cryptType = state.getInt("cryptType", CRYPT_TYPE_INVALID);
|
||||
if (cryptType == CRYPT_TYPE_INVALID
|
||||
|| (cryptType == CRYPT_TYPE_NONE && creds != null)
|
||||
|| (cryptType == CRYPT_TYPE_PASS && (creds == null || !creds.getSlots().has(PasswordSlot.class)))
|
||||
|| (cryptType == CRYPT_TYPE_BIOMETRIC && (creds == null || !creds.getSlots().has(PasswordSlot.class) || !creds.getSlots().has(BiometricSlot.class)))) {
|
||||
throw new RuntimeException(String.format("State of SecuritySetupSlide not properly propagated, cryptType: %d, creds: %s", cryptType, creds));
|
||||
}
|
||||
|
||||
try {
|
||||
_vaultManager.init(creds);
|
||||
} catch (VaultRepositoryException e) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(this, R.string.vault_init_error, e);
|
||||
return;
|
||||
try {
|
||||
_vaultManager.initNew(creds);
|
||||
} catch (VaultRepositoryException e) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(this, R.string.vault_init_error, e);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
_vaultManager.load(creds);
|
||||
} catch (VaultRepositoryException e) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(this, R.string.vault_load_error, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// skip the intro from now on
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package com.beemdevelopment.aegis.ui.intro;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
@ -13,7 +13,6 @@ import androidx.viewpager2.adapter.FragmentStateAdapter;
|
|||
import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.Theme;
|
||||
import com.beemdevelopment.aegis.ui.AegisActivity;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
@ -116,7 +115,7 @@ public abstract class IntroBaseActivity extends AegisActivity implements IntroAc
|
|||
* @param newSlide the next slide that will be shown.
|
||||
* @return whether to block the transition.
|
||||
*/
|
||||
protected boolean onBeforeSlideChanged(Class<? extends SlideFragment> oldSlide, Class<? extends SlideFragment> newSlide) {
|
||||
protected boolean onBeforeSlideChanged(@Nullable Class<? extends SlideFragment> oldSlide, @NonNull Class<? extends SlideFragment> newSlide) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -125,7 +124,7 @@ public abstract class IntroBaseActivity extends AegisActivity implements IntroAc
|
|||
* @param oldSlide the slide that was previously shown.
|
||||
* @param newSlide the slide that is now shown.
|
||||
*/
|
||||
protected void onAfterSlideChanged(Class<? extends SlideFragment> oldSlide, Class<? extends SlideFragment> newSlide) {
|
||||
protected void onAfterSlideChanged(@Nullable Class<? extends SlideFragment> oldSlide, @NonNull Class<? extends SlideFragment> newSlide) {
|
||||
|
||||
}
|
||||
|
||||
|
@ -178,6 +177,13 @@ public abstract class IntroBaseActivity extends AegisActivity implements IntroAc
|
|||
|
||||
_slides.add(type);
|
||||
_slideIndicator.setSlideCount(_slides.size());
|
||||
|
||||
// send 'slide changed' events for the first slide
|
||||
if (_slides.size() == 1) {
|
||||
Class<? extends SlideFragment> slide = _slides.get(0);
|
||||
onBeforeSlideChanged(null, slide);
|
||||
onAfterSlideChanged(null, slide);
|
||||
}
|
||||
}
|
||||
|
||||
private class ScreenSlidePagerAdapter extends FragmentStateAdapter {
|
||||
|
|
|
@ -1,16 +1,109 @@
|
|||
package com.beemdevelopment.aegis.ui.slides;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.importers.AegisImporter;
|
||||
import com.beemdevelopment.aegis.importers.DatabaseImporter;
|
||||
import com.beemdevelopment.aegis.importers.DatabaseImporterException;
|
||||
import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
|
||||
import com.beemdevelopment.aegis.ui.intro.SlideFragment;
|
||||
import com.beemdevelopment.aegis.ui.tasks.ImportFileTask;
|
||||
import com.beemdevelopment.aegis.vault.VaultFileCredentials;
|
||||
import com.beemdevelopment.aegis.vault.VaultRepository;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class WelcomeSlide extends SlideFragment {
|
||||
public static final int CODE_IMPORT_VAULT = 0;
|
||||
|
||||
private boolean _imported;
|
||||
private VaultFileCredentials _creds;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_welcome_slide, container, false);
|
||||
View view = inflater.inflate(R.layout.fragment_welcome_slide, container, false);
|
||||
view.findViewById(R.id.btnImport).setOnClickListener(v -> {
|
||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
intent.setType("*/*");
|
||||
startActivityForResult(intent, CODE_IMPORT_VAULT);
|
||||
});
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == CODE_IMPORT_VAULT && data != null && data.getData() != null) {
|
||||
startImportVault(data.getData());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveIntroState(@NonNull Bundle introState) {
|
||||
introState.putBoolean("imported", _imported);
|
||||
introState.putSerializable("creds", _creds);
|
||||
}
|
||||
|
||||
private void startImportVault(Uri uri) {
|
||||
ImportFileTask.Params params = new ImportFileTask.Params(uri, "intro-import", null);
|
||||
ImportFileTask task = new ImportFileTask(requireContext(), result -> {
|
||||
if (result.getException() != null) {
|
||||
Dialogs.showErrorDialog(requireContext(), R.string.reading_file_error, result.getException());
|
||||
return;
|
||||
}
|
||||
|
||||
try (FileInputStream inStream = new FileInputStream(result.getFile())) {
|
||||
AegisImporter importer = new AegisImporter(requireContext());
|
||||
DatabaseImporter.State state = importer.read(inStream, false);
|
||||
if (state.isEncrypted()) {
|
||||
state.decrypt(requireContext(), new DatabaseImporter.DecryptListener() {
|
||||
@Override
|
||||
protected void onStateDecrypted(DatabaseImporter.State state) {
|
||||
_creds = ((AegisImporter.DecryptedState) state).getCredentials();
|
||||
importVault(result.getFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onError(Exception e) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(requireContext(), R.string.decryption_error, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCanceled() {
|
||||
|
||||
}
|
||||
});
|
||||
} else {
|
||||
importVault(result.getFile());
|
||||
}
|
||||
} catch (DatabaseImporterException | IOException e) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(requireContext(), R.string.intro_import_error_title, e);
|
||||
}
|
||||
});
|
||||
task.execute(getLifecycle(), params);
|
||||
}
|
||||
|
||||
private void importVault(File file) {
|
||||
try (FileInputStream inStream = new FileInputStream(file)) {
|
||||
VaultRepository.writeToFile(requireContext(), inStream);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(requireContext(), R.string.intro_import_error_title, e);
|
||||
return;
|
||||
}
|
||||
|
||||
_imported = true;
|
||||
goToNextSlide();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ public class VaultManager {
|
|||
|
||||
if (_vaultFile != null && !_vaultFile.isEncrypted()) {
|
||||
try {
|
||||
load(_vaultFile, null);
|
||||
loadFrom(_vaultFile, null);
|
||||
} catch (VaultRepositoryException e) {
|
||||
e.printStackTrace();
|
||||
_vaultFile = null;
|
||||
|
@ -76,7 +76,7 @@ public class VaultManager {
|
|||
* Calling this method removes the manager's internal reference to the raw vault file (if it had one).
|
||||
*/
|
||||
@NonNull
|
||||
public VaultRepository init(@Nullable VaultFileCredentials creds) throws VaultRepositoryException {
|
||||
public VaultRepository initNew(@Nullable VaultFileCredentials creds) throws VaultRepositoryException {
|
||||
if (isVaultLoaded()) {
|
||||
throw new IllegalStateException("Vault manager is already initialized");
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ public class VaultManager {
|
|||
* Calling this method removes the manager's internal reference to the raw vault file (if it had one).
|
||||
*/
|
||||
@NonNull
|
||||
public VaultRepository load(@NonNull VaultFile vaultFile, @Nullable VaultFileCredentials creds) throws VaultRepositoryException {
|
||||
public VaultRepository loadFrom(@NonNull VaultFile vaultFile, @Nullable VaultFileCredentials creds) throws VaultRepositoryException {
|
||||
if (isVaultLoaded()) {
|
||||
throw new IllegalStateException("Vault manager is already initialized");
|
||||
}
|
||||
|
@ -116,9 +116,30 @@ public class VaultManager {
|
|||
return getVault();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the vault repository by loading and decrypting the vault file stored in
|
||||
* internal storage, with the given creds. It can only be called if isVaultLoaded()
|
||||
* returns false.
|
||||
*
|
||||
* Calling this method removes the manager's internal reference to the raw vault file (if it had one).
|
||||
*/
|
||||
@NonNull
|
||||
public VaultRepository load(@Nullable VaultFileCredentials creds) throws VaultRepositoryException {
|
||||
if (isVaultLoaded()) {
|
||||
throw new IllegalStateException("Vault manager is already initialized");
|
||||
}
|
||||
|
||||
loadVaultFile();
|
||||
if (isVaultLoaded()) {
|
||||
return _repo;
|
||||
}
|
||||
|
||||
return loadFrom(getVaultFile(), creds);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public VaultRepository unlock(@NonNull VaultFileCredentials creds) throws VaultRepositoryException {
|
||||
VaultRepository repo = load(getVaultFile(), creds);
|
||||
VaultRepository repo = loadFrom(getVaultFile(), creds);
|
||||
startNotificationService();
|
||||
return repo;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue