diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java index 089038184..0edf8be1f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java @@ -80,6 +80,7 @@ import org.briarproject.briar.android.sharing.ShareBlogFragment; import org.briarproject.briar.android.sharing.ShareForumActivity; import org.briarproject.briar.android.sharing.ShareForumFragment; import org.briarproject.briar.android.sharing.SharingModule; +import org.briarproject.briar.android.socialbackup.SetupExplainerFragment; import org.briarproject.briar.android.socialbackup.recover.CustodianRecoveryModeExplainerFragment; import org.briarproject.briar.android.socialbackup.CustodianSelectorFragment; import org.briarproject.briar.android.socialbackup.DistributedBackupActivity; @@ -306,4 +307,6 @@ public interface ActivityComponent { void inject(OwnerRecoveryModeErrorFragment ownerRecoveryModeErrorFragment); void inject(CustodianReturnShardErrorFragment custodianReturnShardErrorFragment); + + void inject(SetupExplainerFragment setupExplainerFragment); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/CustodianSelectorFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/CustodianSelectorFragment.java index 6cfedd9a3..1d2580d0f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/CustodianSelectorFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/CustodianSelectorFragment.java @@ -23,6 +23,7 @@ import java.util.Collection; import javax.inject.Inject; import androidx.annotation.Nullable; +import androidx.lifecycle.ViewModelProvider; import static java.util.Objects.requireNonNull; @@ -32,6 +33,18 @@ public class CustodianSelectorFragment extends ContactSelectorFragment { public static final String TAG = CustodianSelectorFragment.class.getName(); + @Inject + ViewModelProvider.Factory viewModelFactory; + + private SocialBackupSetupViewModel viewModel; + + @Override + public void injectFragment(ActivityComponent component) { + component.inject(this); + viewModel = new ViewModelProvider(requireActivity(), viewModelFactory) + .get(SocialBackupSetupViewModel.class); + } + @Inject CreateBackupController controller; @@ -44,11 +57,6 @@ public class CustodianSelectorFragment extends ContactSelectorFragment { return fragment; } - @Override - public void injectFragment(ActivityComponent component) { - component.inject(this); - } - @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/DistributedBackupActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/DistributedBackupActivity.java index aa9f48275..4070ebed9 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/DistributedBackupActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/DistributedBackupActivity.java @@ -12,7 +12,9 @@ import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BriarActivity; import org.briarproject.briar.android.contactselection.ContactSelectorListener; import org.briarproject.briar.android.fragment.BaseFragment; +import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardViewModel; import org.briarproject.briar.api.socialbackup.BackupMetadata; +import org.briarproject.briar.api.socialbackup.SocialBackup; import org.briarproject.briar.api.socialbackup.SocialBackupManager; import java.util.Collection; @@ -20,25 +22,22 @@ import java.util.List; import javax.inject.Inject; +import androidx.lifecycle.ViewModelProvider; + public class DistributedBackupActivity extends BriarActivity implements BaseFragment.BaseFragmentListener, ContactSelectorListener, - ThresholdDefinedListener, ShardsSentFragment.ShardsSentDismissedListener { - private Collection custodians; + private SocialBackupSetupViewModel viewModel; @Inject - public SocialBackupManager socialBackupManager; - - @Inject - public ContactManager contactManager; - - @Inject - public DatabaseComponent db; + ViewModelProvider.Factory viewModelFactory; @Override public void injectActivity(ActivityComponent component) { component.inject(this); + viewModel = new ViewModelProvider(this, viewModelFactory) + .get(SocialBackupSetupViewModel.class); } @Override @@ -46,19 +45,13 @@ public class DistributedBackupActivity extends BriarActivity implements super.onCreate(savedInstanceState); setContentView(R.layout.activity_distributed_backup); - try { - db.transaction(false, txn -> { - BackupMetadata backupMetadata = - socialBackupManager.getBackupMetadata(txn); - if (backupMetadata == null) throw new DbException(); - ExistingBackupFragment fragment = - ExistingBackupFragment.newInstance(backupMetadata); - showInitialFragment(fragment); - }); - } catch (DbException e) { - // Check the number of contacts in the contacts list > 1 + if (viewModel.haveExistingBackup()) { + ExistingBackupFragment fragment = + ExistingBackupFragment.newInstance(); + showInitialFragment(fragment); + } else { try { - if (contactManager.getContacts().size() < 2) { + if (!viewModel.haveEnoughContacts()) { Toast.makeText(this, R.string.social_backup_not_enough_contacts, Toast.LENGTH_LONG).show(); @@ -70,9 +63,32 @@ public class DistributedBackupActivity extends BriarActivity implements Toast.LENGTH_LONG).show(); finish(); } - CustodianSelectorFragment fragment = - CustodianSelectorFragment.newInstance(); - showInitialFragment(fragment); + showInitialFragment(new SetupExplainerFragment()); + } + + viewModel.getState() + .observe(this, this::onStateChanged); + } + + private void onStateChanged(SocialBackupSetupViewModel.State state) { + switch(state) { + case SUCCESS: + try { + viewModel.createBackup(); + ShardsSentFragment fragment = new ShardsSentFragment(); + showNextFragment(fragment); + } catch (DbException e) { + Toast.makeText(this, + "There was an error when creating the backup", + Toast.LENGTH_LONG).show(); + finish(); + } + break; + case CHOOSING_CUSTODIANS: + CustodianSelectorFragment fragment = + CustodianSelectorFragment.newInstance(); + showNextFragment(fragment); + break; } } @@ -81,32 +97,15 @@ public class DistributedBackupActivity extends BriarActivity implements Toast.makeText(this, String.format("Selected %d contacts", contacts.size()), Toast.LENGTH_SHORT).show(); - custodians = contacts; + viewModel.setCustodians(contacts); ThresholdSelectorFragment fragment = ThresholdSelectorFragment.newInstance(contacts.size()); showNextFragment(fragment); } - @Override - public void thresholdDefined(int threshold) { - try { - db.transaction(false, txn -> { - socialBackupManager - .createBackup(txn, (List) custodians, - threshold); - ShardsSentFragment fragment = new ShardsSentFragment(); - showNextFragment(fragment); - }); - } catch (DbException e) { - Toast.makeText(this, - "There was an error when creating the backup", - Toast.LENGTH_LONG).show(); - finish(); - } - } - @Override public void shardsSentDismissed() { finish(); } + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ExistingBackupFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ExistingBackupFragment.java index c5f7f0912..a2d4c7b3e 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ExistingBackupFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ExistingBackupFragment.java @@ -16,8 +16,11 @@ import org.briarproject.briar.api.socialbackup.BackupMetadata; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.lifecycle.ViewModelProvider; public class ExistingBackupFragment extends BaseFragment { @@ -25,9 +28,21 @@ public class ExistingBackupFragment extends BaseFragment { private static final String CUSTODIANS = "custodians"; public static final String TAG = ExistingBackupFragment.class.getName(); - public static ExistingBackupFragment newInstance( - BackupMetadata backupMetadata) { + @Inject + ViewModelProvider.Factory viewModelFactory; + + private static SocialBackupSetupViewModel viewModel; + + @Override + public void injectFragment(ActivityComponent component) { + component.inject(this); + viewModel = new ViewModelProvider(requireActivity(), viewModelFactory) + .get(SocialBackupSetupViewModel.class); + } + + public static ExistingBackupFragment newInstance() { Bundle bundle = new Bundle(); + BackupMetadata backupMetadata = viewModel.getBackupMetadata(); List custodians = backupMetadata.getCustodians(); ArrayList custodianNames = new ArrayList(); for (Author custodian : custodians) { @@ -85,9 +100,5 @@ public class ExistingBackupFragment extends BaseFragment { return TAG; } - @Override - public void injectFragment(ActivityComponent component) { - component.inject(this); - } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/SetupExplainerFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/SetupExplainerFragment.java new file mode 100644 index 000000000..a5ab84a8b --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/SetupExplainerFragment.java @@ -0,0 +1,54 @@ +package org.briarproject.briar.android.socialbackup; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; + +import org.briarproject.briar.R; +import org.briarproject.briar.android.activity.ActivityComponent; +import org.briarproject.briar.android.fragment.BaseFragment; + +import javax.inject.Inject; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.lifecycle.ViewModelProvider; + +public class SetupExplainerFragment extends BaseFragment { + + public static final String TAG = + SetupExplainerFragment.class.getName(); + + @Inject + ViewModelProvider.Factory viewModelFactory; + + private SocialBackupSetupViewModel viewModel; + + @Override + public void injectFragment(ActivityComponent component) { + component.inject(this); + viewModel = new ViewModelProvider(requireActivity(), viewModelFactory) + .get(SocialBackupSetupViewModel.class); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable + ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = + inflater.inflate(R.layout.fragment_social_backup_setup_explainer, + container, false); + + Button button = view.findViewById(R.id.button); + button.setOnClickListener(e -> viewModel.onExplainerDismissed()); + return view; + } + + @Override + public String getUniqueTag() { + return TAG; + } +} + diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/SocialBackupSetupViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/SocialBackupSetupViewModel.java new file mode 100644 index 000000000..7cb2b16de --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/SocialBackupSetupViewModel.java @@ -0,0 +1,100 @@ +package org.briarproject.briar.android.socialbackup; + +import android.app.Application; + +import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.bramble.api.contact.ContactManager; +import org.briarproject.bramble.api.db.DatabaseComponent; +import org.briarproject.bramble.api.db.DbException; +import org.briarproject.briar.api.socialbackup.BackupMetadata; +import org.briarproject.briar.api.socialbackup.SocialBackupManager; + +import java.util.Collection; +import java.util.List; + +import javax.inject.Inject; + +import androidx.annotation.NonNull; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.MutableLiveData; + +public class SocialBackupSetupViewModel extends AndroidViewModel { + + private final SocialBackupManager socialBackupManager; + private final DatabaseComponent db; + private final ContactManager contactManager; + private BackupMetadata backupMetadata; + private Collection custodians; + private int threshold; + + + public enum State { + EXPLAINING, + CHOOSING_CUSTODIANS, + GETTING_THRESHOLD, + SUCCESS + } + + private final MutableLiveData state = + new MutableLiveData<>(); + + @Inject + public SocialBackupSetupViewModel( + @NonNull Application app, + DatabaseComponent db, + SocialBackupManager socialBackupManager, + ContactManager contactManager + ) { + super(app); + this.socialBackupManager = socialBackupManager; + this.db = db; + this.contactManager = contactManager; + } + + public boolean haveExistingBackup() { + try { + db.transaction(false, txn -> { + backupMetadata = + socialBackupManager.getBackupMetadata(txn); + if (backupMetadata == null) throw new DbException(); + }); + } catch (DbException e) { + return false; + } + return true; + } + + public BackupMetadata getBackupMetadata() { + return backupMetadata; + } + + public boolean haveEnoughContacts() throws DbException { + return (contactManager.getContacts().size() > 1); + } + + public void setCustodians(Collection contacts) { + custodians = contacts; + } + + public void createBackup() throws DbException { + db.transaction(false, txn -> { + socialBackupManager + .createBackup(txn, (List) custodians, + threshold); + }); + } + + public void setThreshold(int threshold) { + this.threshold = threshold; + state.postValue(State.SUCCESS); + } + + public MutableLiveData getState() { + return state; + } + + public void onExplainerDismissed() { + state.postValue(State.CHOOSING_CUSTODIANS); + } +} + diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ThresholdSelectorFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ThresholdSelectorFragment.java index c4b689f39..c152f7773 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ThresholdSelectorFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ThresholdSelectorFragment.java @@ -17,8 +17,11 @@ import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.fragment.BaseFragment; import org.magmacollective.darkcrystal.secretsharingwrapper.SecretSharingWrapper; +import javax.inject.Inject; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.lifecycle.ViewModelProvider; public class ThresholdSelectorFragment extends BaseFragment { @@ -35,6 +38,18 @@ public class ThresholdSelectorFragment extends BaseFragment { private TextView message; private TextView mOfn; + @Inject + ViewModelProvider.Factory viewModelFactory; + + private SocialBackupSetupViewModel viewModel; + + @Override + public void injectFragment(ActivityComponent component) { + component.inject(this); + viewModel = new ViewModelProvider(requireActivity(), viewModelFactory) + .get(SocialBackupSetupViewModel.class); + } + public static ThresholdSelectorFragment newInstance(int numberCustodians) { Bundle bundle = new Bundle(); bundle.putInt(NUMBER_CUSTODIANS, numberCustodians); @@ -103,11 +118,6 @@ public class ThresholdSelectorFragment extends BaseFragment { return TAG; } - @Override - public void injectFragment(ActivityComponent component) { - component.inject(this); - } - @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.define_threshold_actions, menu); @@ -118,11 +128,7 @@ public class ThresholdSelectorFragment extends BaseFragment { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_threshold_defined: - try { - listener.thresholdDefined(threshold); - } catch (DbException e) { - e.printStackTrace(); - } + viewModel.setThreshold(threshold); return true; default: return super.onOptionsItemSelected(item); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardViewModel.java index cad5dd495..8aa67ddf2 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardViewModel.java @@ -53,7 +53,7 @@ public class CustodianReturnShardViewModel extends AndroidViewModel final QrCodeDecoder qrCodeDecoder; private boolean qrCodeRead = false; private WifiManager wifiManager; - private final MutableLiveEvent continueClicked = new MutableLiveEvent<>(); + private final MutableLiveEvent continueClicked = new MutableLiveEvent<>(); private final MutableLiveEvent showCameraFragment = new MutableLiveEvent<>(); private final MutableLiveEvent successDismissed = diff --git a/briar-android/src/main/res/layout/fragment_social_backup_setup_explainer.xml b/briar-android/src/main/res/layout/fragment_social_backup_setup_explainer.xml new file mode 100644 index 000000000..914c808ca --- /dev/null +++ b/briar-android/src/main/res/layout/fragment_social_backup_setup_explainer.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file