diff --git a/briar-android/src/main/AndroidManifest.xml b/briar-android/src/main/AndroidManifest.xml index b590e7acf..30f3ab4a4 100644 --- a/briar-android/src/main/AndroidManifest.xml +++ b/briar-android/src/main/AndroidManifest.xml @@ -134,6 +134,25 @@ + + + + + + + + + getController() { + return controller; + } + + @Override + public String getUniqueTag() { + return TAG; + } + + @Override + protected void onSelectionChanged() { + super.onSelectionChanged(); + if (menu == null) return; + MenuItem item = menu.findItem(R.id.action_contacts_selected); + if (item == null) return; + + BaseContactSelectorAdapter a = adapter; + selectedContacts = a.getSelectedContactIds(); + + int n = selectedContacts.size(); + int min = 2; + boolean enough = n >= min; + + item.setVisible(enough); + if (n == 0) { + Toast.makeText(getContext(), String.format(getString(R.string.select_at_least_n_contacts), min), + Toast.LENGTH_SHORT).show(); + } else if (n < min) { + Toast.makeText(getContext(), String.format(getString(R.string.select_at_least_n_more_contacts), min - n), + Toast.LENGTH_SHORT).show(); + } + } + +} 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 new file mode 100644 index 000000000..42308599d --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/DistributedBackupActivity.java @@ -0,0 +1,77 @@ +package org.briarproject.briar.android.socialbackup; + +import android.os.Bundle; +import android.widget.Toast; + +import androidx.fragment.app.FragmentTransaction; + +import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.bramble.api.db.DatabaseComponent; +import org.briarproject.bramble.api.db.DbException; +import org.briarproject.briar.R; +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.api.socialbackup.SocialBackupManager; + +import java.util.Collection; +import java.util.List; + +import javax.inject.Inject; + +public class DistributedBackupActivity extends BriarActivity implements + BaseFragment.BaseFragmentListener, ContactSelectorListener, + ThresholdDefinedListener, ShardsSentDismissedListener { + + private Collection custodians; + + @Inject + public SocialBackupManager socialBackupManager; + + @Inject + public DatabaseComponent db; + + @Override + public void injectActivity(ActivityComponent component) { + component.inject(this); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_distributed_backup); + // TODO here we should check if we already have a backup + // BackupMetadata backupMetadata = socialBackupManager.getBackupMetadata(); + // if (backupMetadata == null) { + CustodianSelectorFragment fragment = + CustodianSelectorFragment.newInstance(); + // } else { + // display the backup metadata + showInitialFragment(fragment); + } + + @Override + public void contactsSelected(Collection contacts) { + Toast.makeText(this, + String.format("selected %d contacts", contacts.size()), + Toast.LENGTH_SHORT).show(); + custodians = contacts; + ThresholdSelectorFragment fragment = ThresholdSelectorFragment.newInstance(contacts.size()); + showNextFragment(fragment); + } + + @Override + public void thresholdDefined(int threshold) throws DbException { + db.transaction(false, txn -> { + socialBackupManager.createBackup(txn, (List) custodians, threshold); + ShardsSentFragment fragment = new ShardsSentFragment(); + showNextFragment(fragment); + }); + } + + @Override + public void shardsSentDismissed() { + finish(); + } +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/OldDistributedBackupActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/OldDistributedBackupActivity.java new file mode 100644 index 000000000..e74638e3a --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/OldDistributedBackupActivity.java @@ -0,0 +1,39 @@ +package org.briarproject.briar.android.socialbackup; + +import android.os.Bundle; +import android.widget.Toast; + +import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.briar.R; +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 java.util.Collection; + +public class OldDistributedBackupActivity extends BriarActivity + implements BaseFragment.BaseFragmentListener, ContactSelectorListener { + + @Override + public void injectActivity(ActivityComponent component) { + component.inject(this); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_distributed_backup); + +// CustodianDisplayFragment fragment = +// CustodianDisplayFragment.newInstance(); +// +// showInitialFragment(fragment); + } + + @Override + public void contactsSelected(Collection contacts) { + // do nothing + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ShardsSentDismissedListener.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ShardsSentDismissedListener.java new file mode 100644 index 000000000..d7a4e7b36 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ShardsSentDismissedListener.java @@ -0,0 +1,10 @@ +package org.briarproject.briar.android.socialbackup; + +import androidx.annotation.UiThread; + +public interface ShardsSentDismissedListener { + + @UiThread + void shardsSentDismissed(); + +} \ No newline at end of file diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ShardsSentFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ShardsSentFragment.java new file mode 100644 index 000000000..bfac28d3f --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ShardsSentFragment.java @@ -0,0 +1,65 @@ +package org.briarproject.briar.android.socialbackup; + +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +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 androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import static java.util.Objects.requireNonNull; + +public class ShardsSentFragment extends BaseFragment { + + public static final String TAG = ShardsSentFragment.class.getName(); + + protected ShardsSentDismissedListener listener; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requireActivity().setTitle(R.string.title_distributed_backup); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_shards_sent, + container, false); + + Button button = view.findViewById(R.id.button); + button.setOnClickListener(e -> { + listener.shardsSentDismissed(); + }); + + return view; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + listener = (ShardsSentDismissedListener) context; + } + + + @Override + public String getUniqueTag() { + return TAG; + } + + @Override + public void injectFragment(ActivityComponent component) { + component.inject(this); + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ThresholdDefinedListener.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ThresholdDefinedListener.java new file mode 100644 index 000000000..ee99b999a --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ThresholdDefinedListener.java @@ -0,0 +1,12 @@ +package org.briarproject.briar.android.socialbackup; + +import org.briarproject.bramble.api.db.DbException; + +import androidx.annotation.UiThread; + +public interface ThresholdDefinedListener { + + @UiThread + void thresholdDefined(int threshold) throws DbException; + +} \ No newline at end of file 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 new file mode 100644 index 000000000..51c229508 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ThresholdSelectorFragment.java @@ -0,0 +1,169 @@ +package org.briarproject.briar.android.socialbackup; + +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.SeekBar; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.briarproject.bramble.api.db.DbException; +import org.briarproject.briar.R; +import org.briarproject.briar.android.activity.ActivityComponent; +import org.briarproject.briar.android.contactselection.ContactSelectorListener; +import org.briarproject.briar.android.fragment.BaseFragment; +import org.magmacollective.darkcrystal.secretsharingwrapper.SecretSharingWrapper; + +import static java.util.Objects.requireNonNull; + +public class ThresholdSelectorFragment extends BaseFragment { + + public static final String TAG = ThresholdSelectorFragment.class.getName(); + private static final String NUMBER_CUSTODIANS = "numberCustodians"; + + protected ThresholdDefinedListener listener; + + // TODO this should be the actual number of custodians + private int numberOfCustodians; + private int threshold; + private int recommendedThreshold; + private SeekBar seekBar; + private TextView thresholdRepresentation; + private TextView message; + private TextView mOfn; + + public static ThresholdSelectorFragment newInstance(int numberCustodians) { + Bundle bundle = new Bundle(); + bundle.putInt(NUMBER_CUSTODIANS, numberCustodians); + ThresholdSelectorFragment fragment = new ThresholdSelectorFragment(); + fragment.setArguments(bundle); + return fragment; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requireActivity().setTitle(R.string.title_define_threshold); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_select_threshold, + container, false); + Bundle args = requireArguments(); + numberOfCustodians = args.getInt(NUMBER_CUSTODIANS); + + seekBar = view.findViewById(R.id.seekBar); + thresholdRepresentation = view.findViewById(R.id.textViewThresholdRepresentation); + message = view.findViewById(R.id.textViewMessage); + mOfn = view.findViewById(R.id.textViewmOfn); + int max = numberOfCustodians - 3; + seekBar.setMax(max); + seekBar.setOnSeekBarChangeListener(new SeekBarListener()); + recommendedThreshold = SecretSharingWrapper.defaultThreshold(numberOfCustodians); + threshold = recommendedThreshold; + seekBar.setProgress(threshold - 2); + + thresholdRepresentation.setText(buildThresholdRepresentationString()); + setmOfnText(); + return view; +// return super.onCreateView(inflater, container, savedInstanceState); + } + + private void setmOfnText() { + mOfn.setText(String.format("%d of %d contacts needed to recover your account", threshold, numberOfCustodians)); + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + listener = (ThresholdDefinedListener) context; + } + + + @Override + public String getUniqueTag() { + 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); + super.onCreateOptionsMenu(menu, inflater); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_threshold_defined: + try { + listener.thresholdDefined(threshold); + } catch (DbException e) { + e.printStackTrace(); + } + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + private String buildThresholdRepresentationString () { + String thresholdRepresentationText = ""; + for (int i = 0; i < threshold; i++) { + thresholdRepresentationText += getString(R.string.filled_bullet); + } + for (int i = 0; i < (numberOfCustodians - threshold); i++) { + thresholdRepresentationText += getString(R.string.linear_bullet); + } + return thresholdRepresentationText; + } + + private class SeekBarListener implements SeekBar.OnSeekBarChangeListener { + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, + boolean fromUser) { + threshold = progress + 2; + + thresholdRepresentation.setText( + buildThresholdRepresentationString() + ); + setmOfnText(); + + int sanityLevel = SecretSharingWrapper.thresholdSanity(threshold, numberOfCustodians); + int text = R.string.threshold_secure; + if (threshold == recommendedThreshold) text = R.string.threshold_recommended; + if (sanityLevel < -1) text = R.string.threshold_low_insecure; + if (sanityLevel > 0) text = R.string.threshold_high_insecure; + message.setText(text); + // TODO change colour of thresholdRepresentation to green/red based on sanityLevel + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + // do nothing + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + // do nothing + } + + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/creation/CreateBackupController.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/creation/CreateBackupController.java new file mode 100644 index 000000000..04fa8bc13 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/creation/CreateBackupController.java @@ -0,0 +1,26 @@ +package org.briarproject.briar.android.socialbackup.creation; + +import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.sync.GroupId; +import org.briarproject.briar.android.contactselection.ContactSelectorController; +import org.briarproject.briar.android.contactselection.SelectableContactItem; +import org.briarproject.briar.android.controller.handler.ResultExceptionHandler; + +import java.util.Collection; + +import androidx.annotation.Nullable; + +@NotNullByDefault +public interface CreateBackupController + extends ContactSelectorController { + + void createGroup(String name, + ResultExceptionHandler result); + + void sendInvitation(GroupId g, Collection contacts, + @Nullable String text, + ResultExceptionHandler result); + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/creation/CreateBackupControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/creation/CreateBackupControllerImpl.java new file mode 100644 index 000000000..6614d92a3 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/creation/CreateBackupControllerImpl.java @@ -0,0 +1,206 @@ +package org.briarproject.briar.android.socialbackup.creation; + +import org.briarproject.bramble.api.contact.Contact; +import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.bramble.api.contact.ContactManager; +import org.briarproject.bramble.api.crypto.CryptoExecutor; +import org.briarproject.bramble.api.db.DatabaseExecutor; +import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.db.NoSuchContactException; +import org.briarproject.bramble.api.identity.IdentityManager; +import org.briarproject.bramble.api.identity.LocalAuthor; +import org.briarproject.bramble.api.lifecycle.LifecycleManager; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.sync.GroupId; +import org.briarproject.bramble.api.system.Clock; +import org.briarproject.briar.android.contactselection.ContactSelectorControllerImpl; +import org.briarproject.briar.android.controller.handler.ResultExceptionHandler; +import org.briarproject.briar.api.identity.AuthorManager; +import org.briarproject.briar.api.privategroup.GroupMessage; +import org.briarproject.briar.api.privategroup.GroupMessageFactory; +import org.briarproject.briar.api.privategroup.PrivateGroup; +import org.briarproject.briar.api.privategroup.PrivateGroupFactory; +import org.briarproject.briar.api.privategroup.PrivateGroupManager; +import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory; +import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.logging.Logger; + +import javax.annotation.concurrent.Immutable; +import javax.inject.Inject; + +import androidx.annotation.Nullable; + +import static java.util.logging.Level.WARNING; +import static org.briarproject.bramble.util.LogUtils.logException; + +@Immutable +@NotNullByDefault +/** + * Derived from {@link org.briarproject.briar.android.privategroup.invitation.GroupInvitationControllerImpl} + */ +class CreateBackupControllerImpl extends ContactSelectorControllerImpl + implements CreateBackupController { + + private static final Logger LOG = + Logger.getLogger( + CreateBackupControllerImpl.class.getName()); + + private final Executor cryptoExecutor; + private final ContactManager contactManager; + private final IdentityManager identityManager; + private final PrivateGroupFactory groupFactory; + private final GroupMessageFactory groupMessageFactory; + private final PrivateGroupManager groupManager; + private final GroupInvitationFactory groupInvitationFactory; + private final GroupInvitationManager groupInvitationManager; + private final Clock clock; + + @Inject + CreateBackupControllerImpl(@DatabaseExecutor Executor dbExecutor, + @CryptoExecutor Executor cryptoExecutor, + LifecycleManager lifecycleManager, ContactManager contactManager, + IdentityManager identityManager, PrivateGroupFactory groupFactory, + GroupMessageFactory groupMessageFactory, + PrivateGroupManager groupManager, + GroupInvitationFactory groupInvitationFactory, + GroupInvitationManager groupInvitationManager, Clock clock, AuthorManager authorManager) { + super(dbExecutor, lifecycleManager, contactManager, authorManager); + this.cryptoExecutor = cryptoExecutor; + this.contactManager = contactManager; + this.identityManager = identityManager; + this.groupFactory = groupFactory; + this.groupMessageFactory = groupMessageFactory; + this.groupManager = groupManager; + this.groupInvitationFactory = groupInvitationFactory; + this.groupInvitationManager = groupInvitationManager; + this.clock = clock; + } + + @Override + public void createGroup(String name, + ResultExceptionHandler handler) { + runOnDbThread(() -> { + try { + LocalAuthor author = identityManager.getLocalAuthor(); + createGroupAndMessages(author, name, handler); + } catch (DbException e) { + logException(LOG, WARNING, e); + handler.onException(e); + } + }); + } + + private void createGroupAndMessages(LocalAuthor author, String name, + ResultExceptionHandler handler) { + cryptoExecutor.execute(() -> { + LOG.info("Creating group..."); + PrivateGroup group = + groupFactory.createPrivateGroup(name, author); + LOG.info("Creating new join announcement..."); + GroupMessage joinMsg = + groupMessageFactory.createJoinMessage(group.getId(), + clock.currentTimeMillis(), author); + storeGroup(group, joinMsg, handler); + }); + } + + private void storeGroup(PrivateGroup group, GroupMessage joinMsg, + ResultExceptionHandler handler) { + runOnDbThread(() -> { + LOG.info("Adding group to database..."); + try { + groupManager.addPrivateGroup(group, joinMsg, true); + handler.onResult(group.getId()); + } catch (DbException e) { + logException(LOG, WARNING, e); + handler.onException(e); + } + }); + } + + @Override + protected boolean isDisabled(GroupId g, Contact c) { + return false; + } + + @Override + public void sendInvitation(GroupId g, Collection contactIds, + @Nullable String text, + ResultExceptionHandler handler) { + runOnDbThread(() -> { + try { + LocalAuthor localAuthor = identityManager.getLocalAuthor(); + List contacts = new ArrayList<>(); + for (ContactId c : contactIds) { + try { + contacts.add(contactManager.getContact(c)); + } catch (NoSuchContactException e) { + // Continue + } + } + signInvitations(g, localAuthor, contacts, text, handler); + } catch (DbException e) { + logException(LOG, WARNING, e); + handler.onException(e); + } + }); + } + + private void signInvitations(GroupId g, LocalAuthor localAuthor, + Collection contacts, @Nullable String text, + ResultExceptionHandler handler) { + cryptoExecutor.execute(() -> { + long timestamp = clock.currentTimeMillis(); + List contexts = new ArrayList<>(); + for (Contact c : contacts) { + byte[] signature = groupInvitationFactory.signInvitation(c, g, + timestamp, localAuthor.getPrivateKey()); + contexts.add(new InvitationContext(c.getId(), timestamp, + signature)); + } + sendInvitations(g, contexts, text, handler); + }); + } + + private void sendInvitations(GroupId g, + Collection contexts, @Nullable String text, + ResultExceptionHandler handler) { + runOnDbThread(() -> { + try { + for (InvitationContext context : contexts) { + try { + groupInvitationManager.sendInvitation(g, + context.contactId, text, context.timestamp, + context.signature); + } catch (NoSuchContactException e) { + // Continue + } + } + //noinspection ConstantConditions + handler.onResult(null); + } catch (DbException e) { + logException(LOG, WARNING, e); + handler.onException(e); + } + }); + } + + private static class InvitationContext { + + private final ContactId contactId; + private final long timestamp; + private final byte[] signature; + + private InvitationContext(ContactId contactId, long timestamp, + byte[] signature) { + this.contactId = contactId; + this.timestamp = timestamp; + this.signature = signature; + } + } +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/creation/CreateBackupModule.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/creation/CreateBackupModule.java new file mode 100644 index 000000000..76d64741b --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/creation/CreateBackupModule.java @@ -0,0 +1,18 @@ +package org.briarproject.briar.android.socialbackup.creation; + +import org.briarproject.briar.android.activity.ActivityScope; + +import dagger.Module; +import dagger.Provides; + +@Module +public class CreateBackupModule { + + @ActivityScope + @Provides + CreateBackupController provideCreateGroupController( + CreateBackupControllerImpl createBackupController) { + return createBackupController; + } + +} diff --git a/briar-android/src/main/res/drawable/ic_baseline_done_outline_24.xml b/briar-android/src/main/res/drawable/ic_baseline_done_outline_24.xml new file mode 100644 index 000000000..6601032a8 --- /dev/null +++ b/briar-android/src/main/res/drawable/ic_baseline_done_outline_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/briar-android/src/main/res/drawable/ic_pie_2_of_3.xml b/briar-android/src/main/res/drawable/ic_pie_2_of_3.xml new file mode 100644 index 000000000..7f65b7e03 --- /dev/null +++ b/briar-android/src/main/res/drawable/ic_pie_2_of_3.xml @@ -0,0 +1,39 @@ + + + + + + + + diff --git a/briar-android/src/main/res/drawable/ic_pie_2_of_5.xml b/briar-android/src/main/res/drawable/ic_pie_2_of_5.xml new file mode 100644 index 000000000..707dd584d --- /dev/null +++ b/briar-android/src/main/res/drawable/ic_pie_2_of_5.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + diff --git a/briar-android/src/main/res/drawable/ic_pie_3_of_5.xml b/briar-android/src/main/res/drawable/ic_pie_3_of_5.xml new file mode 100644 index 000000000..dab65ef9f --- /dev/null +++ b/briar-android/src/main/res/drawable/ic_pie_3_of_5.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + diff --git a/briar-android/src/main/res/drawable/ic_pie_4_of_5.xml b/briar-android/src/main/res/drawable/ic_pie_4_of_5.xml new file mode 100644 index 000000000..3300cd2bf --- /dev/null +++ b/briar-android/src/main/res/drawable/ic_pie_4_of_5.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + diff --git a/briar-android/src/main/res/layout/activity_distributed_backup.xml b/briar-android/src/main/res/layout/activity_distributed_backup.xml new file mode 100644 index 000000000..9d54ad6f2 --- /dev/null +++ b/briar-android/src/main/res/layout/activity_distributed_backup.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/briar-android/src/main/res/layout/activity_preview_recovery_custodian1.xml b/briar-android/src/main/res/layout/activity_preview_recovery_custodian1.xml new file mode 100644 index 000000000..3feffb79a --- /dev/null +++ b/briar-android/src/main/res/layout/activity_preview_recovery_custodian1.xml @@ -0,0 +1,21 @@ + + + + + + + + + \ No newline at end of file diff --git a/briar-android/src/main/res/layout/activity_preview_recovery_custodian2.xml b/briar-android/src/main/res/layout/activity_preview_recovery_custodian2.xml new file mode 100644 index 000000000..f8a1cd2bc --- /dev/null +++ b/briar-android/src/main/res/layout/activity_preview_recovery_custodian2.xml @@ -0,0 +1,21 @@ + + + + + + + + + \ No newline at end of file diff --git a/briar-android/src/main/res/layout/activity_preview_recovery_owner1.xml b/briar-android/src/main/res/layout/activity_preview_recovery_owner1.xml new file mode 100644 index 000000000..1e8fec49a --- /dev/null +++ b/briar-android/src/main/res/layout/activity_preview_recovery_owner1.xml @@ -0,0 +1,21 @@ + + + + + + + + + \ No newline at end of file diff --git a/briar-android/src/main/res/layout/activity_preview_recovery_owner2.xml b/briar-android/src/main/res/layout/activity_preview_recovery_owner2.xml new file mode 100644 index 000000000..9496e994e --- /dev/null +++ b/briar-android/src/main/res/layout/activity_preview_recovery_owner2.xml @@ -0,0 +1,21 @@ + + + + + + + + + \ No newline at end of file diff --git a/briar-android/src/main/res/layout/activity_preview_recovery_owner3.xml b/briar-android/src/main/res/layout/activity_preview_recovery_owner3.xml new file mode 100644 index 000000000..57ea4820b --- /dev/null +++ b/briar-android/src/main/res/layout/activity_preview_recovery_owner3.xml @@ -0,0 +1,21 @@ + + + + + + + + + \ No newline at end of file diff --git a/briar-android/src/main/res/layout/activity_preview_recovery_owner4.xml b/briar-android/src/main/res/layout/activity_preview_recovery_owner4.xml new file mode 100644 index 000000000..783d045a0 --- /dev/null +++ b/briar-android/src/main/res/layout/activity_preview_recovery_owner4.xml @@ -0,0 +1,21 @@ + + + + + + + + + \ No newline at end of file diff --git a/briar-android/src/main/res/layout/activity_preview_welcome.xml b/briar-android/src/main/res/layout/activity_preview_welcome.xml new file mode 100644 index 000000000..293518b6b --- /dev/null +++ b/briar-android/src/main/res/layout/activity_preview_welcome.xml @@ -0,0 +1,21 @@ + + + + + + + + + \ No newline at end of file diff --git a/briar-android/src/main/res/layout/activity_welcome.xml b/briar-android/src/main/res/layout/activity_welcome.xml new file mode 100644 index 000000000..9c88ce321 --- /dev/null +++ b/briar-android/src/main/res/layout/activity_welcome.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/briar-android/src/main/res/layout/fragment_account_recovered.xml b/briar-android/src/main/res/layout/fragment_account_recovered.xml new file mode 100644 index 000000000..fcfa9cfa1 --- /dev/null +++ b/briar-android/src/main/res/layout/fragment_account_recovered.xml @@ -0,0 +1,50 @@ + + + +