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 7b3cd6300..35bfe339f 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 @@ -2,32 +2,37 @@ package org.briarproject.briar.android.socialbackup; import android.content.Context; import android.os.Bundle; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.style.ImageSpan; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import org.briarproject.bramble.api.db.DbException; -import org.briarproject.bramble.api.identity.Author; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; +import org.briarproject.briar.android.contact.ContactListAdapter; +import org.briarproject.briar.android.contact.ContactListItem; +import org.briarproject.briar.android.contact.OnContactClickListener; import org.briarproject.briar.android.fragment.BaseFragment; -import org.briarproject.briar.api.socialbackup.BackupMetadata; +import org.briarproject.briar.android.view.BriarRecyclerView; -import java.util.ArrayList; -import java.util.List; +import java.util.Arrays; import javax.inject.Inject; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.lifecycle.ViewModelProvider; +import androidx.recyclerview.widget.LinearLayoutManager; -public class ExistingBackupFragment extends BaseFragment { +public class ExistingBackupFragment extends BaseFragment implements + OnContactClickListener { - private static final String THRESHOLD = "threshold"; - private static final String CUSTODIANS = "custodians"; public static final String TAG = ExistingBackupFragment.class.getName(); + private final ContactListAdapter adapter = new ContactListAdapter(this); + private BriarRecyclerView list; @Inject ViewModelProvider.Factory viewModelFactory; @@ -52,32 +57,42 @@ public class ExistingBackupFragment extends BaseFragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + // change toolbar text (relevant when navigating back to this fragment) + requireActivity().setTitle(R.string.social_backup_trusted_contacts); + View view = inflater.inflate(R.layout.fragment_existing_backup, container, false); - BackupMetadata backupMetadata = viewModel.getBackupMetadata(); - List custodians = backupMetadata.getCustodians(); + viewModel.loadCustodianList(); + list = view.findViewById(R.id.custodianList); + list.setLayoutManager(new LinearLayoutManager(getActivity())); + list.setAdapter(adapter); + list.setEmptyText(R.string.no_contacts); - StringBuilder custodianNamesString = new StringBuilder(); - for (Author custodian : custodians) { - custodianNamesString - .append("• ") - .append(custodian.getName()) - .append("\n"); - } + viewModel.getContactListItems().observe(getViewLifecycleOwner(), + result -> result.onError(this::handleException) + .onSuccess(adapter::submitList) + ); - TextView textViewThreshold = view.findViewById(R.id.textViewThreshold); - textViewThreshold.setText(getString(R.string.existing_backup_explain, - backupMetadata.getThreshold())); - TextView textViewCustodians = - view.findViewById(R.id.textViewCustodians); - textViewCustodians.setText(custodianNamesString); + int threshold = viewModel.getThresholdFromExistingBackup(); + int numberOfCustodians = + viewModel.getNumberOfCustodiansFromExistingBackup(); + + TextView mOfn = view.findViewById(R.id.textViewThreshold); + mOfn.setText(String.format( + getString(R.string.threshold_m_of_n), threshold, + numberOfCustodians)); + + TextView thresholdRepresentation = + view.findViewById(R.id.textViewThresholdRepresentation); + thresholdRepresentation.setText( + buildThresholdRepresentationString(threshold, + numberOfCustodians)); return view; } @Override public void onAttach(Context context) { super.onAttach(context); -// listener = (ShardsSentDismissedListener) context; } @Override @@ -85,4 +100,27 @@ public class ExistingBackupFragment extends BaseFragment { return TAG; } + @Override + public void onItemClick(View view, ContactListItem item) { + } + + private SpannableStringBuilder buildThresholdRepresentationString( + int threshold, int numberOfCustodians) { + char[] charArray = new char[numberOfCustodians]; + Arrays.fill(charArray, ' '); + SpannableStringBuilder string = + new SpannableStringBuilder(new String(charArray)); + + for (int i = 0; i < numberOfCustodians; i++) { + int drawable = i < threshold + ? R.drawable.ic_custodian_required + : R.drawable.ic_custodian_optional; + string.setSpan(new ImageSpan(getContext(), drawable), i, + i + 1, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + // If we have more than 6, split it on two lines + if (numberOfCustodians > 6) string.insert(4, "\n"); + return string; + } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/SocialBackupSetupActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/SocialBackupSetupActivity.java index 0c616cb88..134fc0f8f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/SocialBackupSetupActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/SocialBackupSetupActivity.java @@ -12,6 +12,7 @@ import org.briarproject.briar.android.contactselection.ContactSelectorListener; import org.briarproject.briar.android.fragment.BaseFragment; import java.util.Collection; +import java.util.List; import javax.inject.Inject; @@ -84,7 +85,7 @@ public class SocialBackupSetupActivity extends BriarActivity implements Toast.makeText(this, String.format("Selected %d contacts", contacts.size()), Toast.LENGTH_SHORT).show(); - viewModel.setCustodians(contacts); + viewModel.setCustodians((List) contacts); ThresholdSelectorFragment fragment = ThresholdSelectorFragment.newInstance(contacts.size()); showNextFragment(fragment); 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 index 1b80dfa2e..b9050d4e5 100644 --- 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 @@ -2,29 +2,37 @@ package org.briarproject.briar.android.socialbackup; import android.app.Application; +import org.briarproject.bramble.api.connection.ConnectionRegistry; 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.DatabaseExecutor; import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.event.EventBus; +import org.briarproject.bramble.api.lifecycle.LifecycleManager; +import org.briarproject.bramble.api.system.AndroidExecutor; +import org.briarproject.briar.android.contact.ContactsViewModel; +import org.briarproject.briar.api.conversation.ConversationManager; +import org.briarproject.briar.api.identity.AuthorManager; import org.briarproject.briar.api.socialbackup.BackupMetadata; import org.briarproject.briar.api.socialbackup.SocialBackupManager; -import java.util.Collection; +import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executor; import javax.inject.Inject; import androidx.annotation.NonNull; -import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.MutableLiveData; -public class SocialBackupSetupViewModel extends AndroidViewModel { +public class SocialBackupSetupViewModel extends ContactsViewModel { private final SocialBackupManager socialBackupManager; private final DatabaseComponent db; private final ContactManager contactManager; private BackupMetadata backupMetadata; - private Collection custodians; + private List custodians; private int threshold; @@ -38,20 +46,39 @@ public class SocialBackupSetupViewModel extends AndroidViewModel { private final MutableLiveData state = new MutableLiveData<>(); - @Inject public SocialBackupSetupViewModel( @NonNull Application app, DatabaseComponent db, + @DatabaseExecutor Executor dbExecutor, + LifecycleManager lifecycleManager, + AuthorManager authorManager, + ConversationManager conversationManager, + ConnectionRegistry connectionRegistry, + EventBus eventBus, + AndroidExecutor androidExecutor, SocialBackupManager socialBackupManager, ContactManager contactManager ) { - super(app); + super(app, dbExecutor, lifecycleManager, db, androidExecutor, + contactManager, authorManager, conversationManager, + connectionRegistry, eventBus); + this.socialBackupManager = socialBackupManager; this.db = db; this.contactManager = contactManager; } + public void loadCustodianList() { + try { + custodians = db.transactionWithResult(true, + socialBackupManager::getCustodianContactIds); + } catch (DbException e) { + custodians = new ArrayList<>(); + } + loadContacts(); + } + public boolean haveExistingBackup() { try { backupMetadata = db.transactionWithNullableResult(true, @@ -62,15 +89,15 @@ public class SocialBackupSetupViewModel extends AndroidViewModel { return (backupMetadata != null); } - public BackupMetadata getBackupMetadata() { - return backupMetadata; - } +// public BackupMetadata getBackupMetadata() { +// return backupMetadata; +// } public boolean haveEnoughContacts() throws DbException { return (contactManager.getContacts().size() > 1); } - public void setCustodians(Collection contacts) { + public void setCustodians(List contacts) { custodians = contacts; } @@ -102,5 +129,19 @@ public class SocialBackupSetupViewModel extends AndroidViewModel { public void onExplainerDismissed() { state.postValue(State.CHOOSING_CUSTODIANS); } + + @Override + protected boolean displayContact(ContactId contactId) { + // Check if contact holds a backup piece + return custodians.contains(contactId); + } + + public int getThresholdFromExistingBackup() { + return backupMetadata.getThreshold(); + } + + public int getNumberOfCustodiansFromExistingBackup() { + return backupMetadata.getCustodians().size(); + } } 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 b61126d8a..c124c8227 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 @@ -123,7 +123,6 @@ public class ThresholdSelectorFragment extends BaseFragment { super.onAttach(context); } - @Override public String getUniqueTag() { return TAG; diff --git a/briar-android/src/main/res/layout/fragment_existing_backup.xml b/briar-android/src/main/res/layout/fragment_existing_backup.xml index 4b4c79e81..6fc28568a 100644 --- a/briar-android/src/main/res/layout/fragment_existing_backup.xml +++ b/briar-android/src/main/res/layout/fragment_existing_backup.xml @@ -11,25 +11,35 @@ android:paddingBottom="@dimen/margin_medium" android:layout_height="match_parent"> + + app:layout_constraintTop_toBottomOf="@+id/textViewThresholdRepresentation" /> + + + \ No newline at end of file diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index 51b671713..d9d74fad8 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -719,10 +719,6 @@ Create new account Restore account from backup - - \u25CF - \u25CB - Social Backup Restore Account @@ -738,4 +734,7 @@ To make a social backup, you need at least 2 contacts in your contacts list There was an error reading your contacts list + + + Trusted Contacts