diff --git a/briar-android/src/main/AndroidManifest.xml b/briar-android/src/main/AndroidManifest.xml index 76f00e6c4..576288372 100644 --- a/briar-android/src/main/AndroidManifest.xml +++ b/briar-android/src/main/AndroidManifest.xml @@ -139,16 +139,7 @@ - - - - + + android:parentActivityName="org.briarproject.briar.android.account.SetupActivity"> + android:value="org.briarproject.briar.android.account.SetupActivity" /> viewModel.recoverClicked()); + return v; } @@ -75,6 +79,8 @@ public class AuthorNameFragment extends SetupFragment { boolean enabled = authorNameLength > 0 && !error; authorNameInput.setOnEditorActionListener(enabled ? this : null); nextButton.setEnabled(enabled); + recoverButton.setEnabled(!enabled); +// recoverButton.setVisibility(enabled ? View.INVISIBLE : View.VISIBLE); } @Override diff --git a/briar-android/src/main/java/org/briarproject/briar/android/account/NewOrRecoverActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/account/NewOrRecoverActivity.java deleted file mode 100644 index 7c8a9a5b0..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/account/NewOrRecoverActivity.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.briarproject.briar.android.account; - -import android.content.Intent; -import android.os.Bundle; - -import org.briarproject.briar.R; -import org.briarproject.briar.android.activity.ActivityComponent; -import org.briarproject.briar.android.activity.BaseActivity; -import org.briarproject.briar.android.fragment.BaseFragment; -import org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardActivity; - -import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; -import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; -import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; -import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; - -public class NewOrRecoverActivity extends BaseActivity implements - BaseFragment.BaseFragmentListener, SetupNewAccountChosenListener, - RecoverAccountListener { - - @Override - public void injectActivity(ActivityComponent component) { - component.inject(this); - } - - @Override - public void onCreate(Bundle state) { - super.onCreate(state); - // fade-in after splash screen instead of default animation - // TODO the fade in is not working - overridePendingTransition(R.anim.fade_in, R.anim.fade_out); - setContentView(R.layout.activity_fragment_container); - NewOrRecoverFragment fragment = NewOrRecoverFragment.newInstance(); - showInitialFragment(fragment); - } - - @Override - public void setupNewAccountChosen() { - finish(); - Intent i = new Intent(this, SetupActivity.class); - i.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP | - FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_TASK_ON_HOME); - startActivity(i); - } - - @Override - public void recoverAccountChosen() { - finish(); - Intent i = new Intent(this, OwnerReturnShardActivity.class); - i.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP | - FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_TASK_ON_HOME); - startActivity(i); - } - - @Override - @Deprecated - public void runOnDbThread(Runnable runnable) { - throw new RuntimeException("Don't use this deprecated method here."); - } -} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/account/NewOrRecoverFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/account/NewOrRecoverFragment.java deleted file mode 100644 index a6055afac..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/account/NewOrRecoverFragment.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.briarproject.briar.android.account; - -import android.content.Context; -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 androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -public class NewOrRecoverFragment extends BaseFragment { - - public static final String TAG = NewOrRecoverFragment.class.getName(); - - protected SetupNewAccountChosenListener setupNewAccountListener; - protected RecoverAccountListener recoverAccountListener; - - public static NewOrRecoverFragment newInstance() { - Bundle bundle = new Bundle(); - NewOrRecoverFragment fragment = new NewOrRecoverFragment(); - fragment.setArguments(bundle); - return fragment; - } - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - requireActivity().setTitle(R.string.setup_title); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable - ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_new_or_recover, - container, false); - Button newAccountButton = view.findViewById(R.id.buttonSetupNewAccount); - newAccountButton.setOnClickListener(e -> { - setupNewAccountListener.setupNewAccountChosen(); - }); - - Button recoverAccountButton = view.findViewById(R.id.buttonRestoreAccount); - recoverAccountButton.setOnClickListener(e -> { - recoverAccountListener.recoverAccountChosen(); - }); - return view; - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - setupNewAccountListener = (SetupNewAccountChosenListener) context; - recoverAccountListener = (RecoverAccountListener) 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/account/SetupActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/account/SetupActivity.java index 68d90ad95..793e57369 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/account/SetupActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/account/SetupActivity.java @@ -10,6 +10,7 @@ import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BaseActivity; import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener; +import org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardActivity; import javax.annotation.Nullable; import javax.inject.Inject; @@ -25,6 +26,7 @@ import static org.briarproject.briar.android.account.SetupViewModel.State.AUTHOR import static org.briarproject.briar.android.account.SetupViewModel.State.CREATED; import static org.briarproject.briar.android.account.SetupViewModel.State.DOZE; import static org.briarproject.briar.android.account.SetupViewModel.State.FAILED; +import static org.briarproject.briar.android.account.SetupViewModel.State.RECOVER; import static org.briarproject.briar.android.account.SetupViewModel.State.SET_PASSWORD; @MethodsNotNullByDefault @@ -60,6 +62,8 @@ public class SetupActivity extends BaseActivity showPasswordFragment(); } else if (state == DOZE) { showDozeFragment(); + } else if (state == RECOVER) { + recover(); } else if (state == CREATED || state == FAILED) { // TODO: Show an error if failed showApp(); @@ -84,6 +88,13 @@ public class SetupActivity extends BaseActivity overridePendingTransition(R.anim.screen_new_in, R.anim.screen_old_out); } + void recover () { +// finish(); + Intent i = new Intent(this, OwnerReturnShardActivity.class); + i.addFlags(FLAG_ACTIVITY_NEW_TASK); + startActivity(i); + } + @Override @Deprecated public void runOnDbThread(Runnable runnable) { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/account/SetupViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/account/SetupViewModel.java index 845075e8d..1ccdde55d 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/account/SetupViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/account/SetupViewModel.java @@ -25,13 +25,14 @@ import static org.briarproject.briar.android.account.SetupViewModel.State.AUTHOR import static org.briarproject.briar.android.account.SetupViewModel.State.CREATED; import static org.briarproject.briar.android.account.SetupViewModel.State.DOZE; import static org.briarproject.briar.android.account.SetupViewModel.State.FAILED; +import static org.briarproject.briar.android.account.SetupViewModel.State.RECOVER; import static org.briarproject.briar.android.account.SetupViewModel.State.SET_PASSWORD; @MethodsNotNullByDefault @ParametersNotNullByDefault public class SetupViewModel extends AndroidViewModel { - enum State {AUTHOR_NAME, SET_PASSWORD, DOZE, CREATED, FAILED} + enum State {AUTHOR_NAME, SET_PASSWORD, DOZE, CREATED, FAILED, RECOVER} private static final Logger LOG = getLogger(SetupActivity.class.getName()); @@ -117,4 +118,9 @@ class SetupViewModel extends AndroidViewModel { } }); } + + public void recoverClicked() { + LOG.info("RECOVER CLICKED ***"); + state.postEvent(RECOVER); + } } 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 7b3b9a2ad..4bb9c0caf 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 @@ -7,8 +7,6 @@ import org.briarproject.briar.android.AndroidComponent; import org.briarproject.briar.android.StartupFailureActivity; import org.briarproject.briar.android.account.AuthorNameFragment; import org.briarproject.briar.android.account.DozeFragment; -import org.briarproject.briar.android.account.NewOrRecoverActivity; -import org.briarproject.briar.android.account.NewOrRecoverFragment; import org.briarproject.briar.android.account.SetPasswordFragment; import org.briarproject.briar.android.account.SetupActivity; import org.briarproject.briar.android.account.UnlockActivity; @@ -92,20 +90,19 @@ 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; +import org.briarproject.briar.android.socialbackup.SocialBackupSetupActivity; import org.briarproject.briar.android.socialbackup.ExistingBackupFragment; import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardActivity; import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardErrorFragment; import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardFragment; -import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardSuccessFragment; import org.briarproject.briar.android.socialbackup.recover.OwnerRecoveryModeErrorFragment; import org.briarproject.briar.android.socialbackup.recover.OwnerRecoveryModeExplainerFragment; import org.briarproject.briar.android.socialbackup.recover.OwnerRecoveryModeMainFragment; import org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardActivity; import org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardFragment; -import org.briarproject.briar.android.socialbackup.ShardsSentFragment; import org.briarproject.briar.android.socialbackup.ThresholdSelectorFragment; import org.briarproject.briar.android.socialbackup.creation.CreateBackupModule; import org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardSuccessFragment; @@ -114,7 +111,6 @@ import org.briarproject.briar.android.socialbackup.recover.RestoreAccountDozeFra import org.briarproject.briar.android.socialbackup.recover.RestoreAccountSetPasswordFragment; import org.briarproject.briar.android.splash.SplashScreenActivity; import org.briarproject.briar.android.test.TestDataActivity; -import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount; import dagger.Component; @@ -217,8 +213,6 @@ public interface ActivityComponent { void inject(CrashReportActivity crashReportActivity); - void inject(NewOrRecoverActivity newOrRecoverActivity); - void inject(CustodianReturnShardActivity custodianReturnShardActivity); void inject(OwnerReturnShardActivity ownerReturnShardActivity); @@ -295,28 +289,22 @@ public interface ActivityComponent { void inject(ThresholdSelectorFragment thresholdSelectorFragment); - void inject(DistributedBackupActivity distributedBackupActivity); + void inject(SocialBackupSetupActivity distributedBackupActivity); void inject(DatabaseComponent databaseComponent); void inject(CustodianSelectorFragment custodianSelectorFragment); - void inject(ShardsSentFragment shardsSentFragment); - void inject(OwnerRecoveryModeExplainerFragment ownerRecoveryModeExplainerFragment); void inject(ExistingBackupFragment existingBackupFragment); - void inject(NewOrRecoverFragment newOrRecoverFragment); - void inject(CustodianRecoveryModeExplainerFragment custodianRecoveryModeExplainerFragment); void inject(CustodianReturnShardFragment custodianReturnShardFragment); void inject(OwnerReturnShardFragment ownerReturnShardFragment); - void inject(CustodianReturnShardSuccessFragment custodianReturnShardSuccessFragment); - void inject(RestoreAccountSetPasswordFragment restoreAccountSetPasswordFragment); void inject(RestoreAccountDozeFragment restoreAccountDozeFragment); @@ -340,4 +328,6 @@ public interface ActivityComponent { void inject(RevokeRemoteWipeSuccessFragment revokeRemoteWipeSuccessFragment); void inject(RemoteWipeSetupExplainerFragment remoteWipeSetupExplainerFragment); + + void inject(SetupExplainerFragment setupExplainerFragment); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/StartupActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/login/StartupActivity.java index 0303d7447..faf469c28 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/login/StartupActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/login/StartupActivity.java @@ -7,7 +7,6 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.briar.R; import org.briarproject.briar.android.BriarService; -import org.briarproject.briar.android.account.NewOrRecoverActivity; import org.briarproject.briar.android.account.SetupActivity; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BaseActivity; @@ -108,7 +107,7 @@ public class StartupActivity extends BaseActivity implements private void onAccountDeleted() { setResult(RESULT_CANCELED); finish(); - Intent i = new Intent(this, NewOrRecoverActivity.class); + Intent i = new Intent(this, SetupActivity.class); i.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_TASK_ON_HOME); startActivity(i); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java index 68c70ccaa..4434e484d 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java @@ -159,7 +159,8 @@ public class NavDrawerActivity extends BriarActivity implements @Override public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); - navDrawerViewModel.checkTransportsOnboarding(); +// navDrawerViewModel.checkTransportsOnboarding(); + navDrawerViewModel.checkSocialBackupOnboarding(); } }; drawerLayout.addDrawerListener(drawerToggle); @@ -168,9 +169,12 @@ public class NavDrawerActivity extends BriarActivity implements initializeTransports(); transportsView.setAdapter(transportsAdapter); - observeOnce(navDrawerViewModel.showTransportsOnboarding(), this, show -> - observeOnce(torIcon, this, imageView -> - showTransportsOnboarding(show, imageView))); +// observeOnce(navDrawerViewModel.showTransportsOnboarding(), this, show -> +// observeOnce(torIcon, this, imageView -> +// showTransportsOnboarding(show, imageView))); + + observeOnce(navDrawerViewModel.showSocialBackupOnboarding(), this, + this::showSocialBackupOnboarding); lockManager.isLockable().observe(this, this::setLockVisible); @@ -469,6 +473,22 @@ public class NavDrawerActivity extends BriarActivity implements } } + private void showSocialBackupOnboarding(boolean show) { + if (show) { + new MaterialTapTargetPrompt.Builder(NavDrawerActivity.this, + R.style.OnboardingDialogTheme) + .setTarget(R.id.nav_btn_settings) + .setPrimaryText(R.string.social_backup_onboarding_title) + .setSecondaryText(R.string.social_backup_onboarding_long) + .setFocalRadius((float) 350) + .setFocalPadding((float) 100) + .setBackgroundColour( + ContextCompat.getColor(this, R.color.briar_primary)) + .show(); + navDrawerViewModel.socialBackupOnboardingShown(); + } + } + private static class Transport { private final TransportId id; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java index 132cc0b9f..d38dac6ba 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java @@ -39,6 +39,8 @@ public class NavDrawerViewModel extends DbViewModel { private static final String EXPIRY_DATE_WARNING = "expiryDateWarning"; private static final String SHOW_TRANSPORTS_ONBOARDING = "showTransportsOnboarding"; + private static final String SHOW_SOCIAL_BACKUP_ONBOARDING = + "showSocialBackupOnboarding"; private final SettingsManager settingsManager; @@ -48,6 +50,8 @@ public class NavDrawerViewModel extends DbViewModel { new MutableLiveData<>(); private final MutableLiveData showTransportsOnboarding = new MutableLiveData<>(); + private final MutableLiveData showSocialBackupOnboarding = + new MutableLiveData<>(); @Inject NavDrawerViewModel(Application app, @@ -142,6 +146,11 @@ public class NavDrawerViewModel extends DbViewModel { return showTransportsOnboarding; } + @UiThread + LiveData showSocialBackupOnboarding() { + return showSocialBackupOnboarding; + } + @UiThread void checkTransportsOnboarding() { if (showTransportsOnboarding.getValue() != null) return; @@ -171,4 +180,35 @@ public class NavDrawerViewModel extends DbViewModel { } }); } + + @UiThread + void checkSocialBackupOnboarding() { + if (showSocialBackupOnboarding.getValue() != null) return; + runOnDbThread(() -> { + try { + Settings settings = + settingsManager.getSettings(SETTINGS_NAMESPACE); + boolean show = + settings.getBoolean(SHOW_SOCIAL_BACKUP_ONBOARDING, + true); + showSocialBackupOnboarding.postValue(show); + } catch (DbException e) { + logException(LOG, WARNING, e); + } + }); + } + + @UiThread + void socialBackupOnboardingShown() { + showSocialBackupOnboarding.setValue(false); + runOnDbThread(() -> { + try { + Settings settings = new Settings(); + settings.putBoolean(SHOW_SOCIAL_BACKUP_ONBOARDING, false); + settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE); + } catch (DbException e) { + logException(LOG, WARNING, e); + } + }); + } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsActivity.java index 747bc9895..8225f479b 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsActivity.java @@ -1,10 +1,12 @@ package org.briarproject.briar.android.settings; import android.content.Intent; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.view.MenuItem; import android.view.View; +import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; @@ -12,6 +14,7 @@ import org.briarproject.bramble.api.FeatureFlags; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BriarActivity; +import org.briarproject.briar.android.navdrawer.NavDrawerActivity; import org.briarproject.briar.android.util.UiUtils; import org.briarproject.briar.android.view.AuthorView; @@ -19,11 +22,15 @@ import javax.inject.Inject; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; +import androidx.core.content.ContextCompat; import androidx.lifecycle.ViewModelProvider; +import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; import de.hdodenhof.circleimageview.CircleImageView; +import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt; import static android.widget.Toast.LENGTH_LONG; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_AVATAR_IMAGE; +import static org.briarproject.briar.android.util.UiUtils.resolveColorAttribute; public class SettingsActivity extends BriarActivity { 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..6f05c0d0f 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); @@ -78,15 +86,19 @@ public class CustodianSelectorFragment extends ContactSelectorFragment { int n = selectedContacts.size(); int min = 2; - boolean enough = n >= min; + int max = 7; + boolean amountIsValid = (n >= min) && (n <= max); - item.setVisible(enough); + item.setVisible(amountIsValid); 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(); + } else if (n > max) { + Toast.makeText(getContext(), String.format(getString(R.string.select_no_more_than_n_contacts), max), + 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 deleted file mode 100644 index aa9f48275..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/DistributedBackupActivity.java +++ /dev/null @@ -1,112 +0,0 @@ -package org.briarproject.briar.android.socialbackup; - -import android.os.Bundle; -import android.widget.Toast; - -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.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.BackupMetadata; -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, - ShardsSentFragment.ShardsSentDismissedListener { - - private Collection custodians; - - @Inject - public SocialBackupManager socialBackupManager; - - @Inject - public ContactManager contactManager; - - @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); - - 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 - try { - if (contactManager.getContacts().size() < 2) { - Toast.makeText(this, - R.string.social_backup_not_enough_contacts, - Toast.LENGTH_LONG).show(); - finish(); - } - } catch (DbException dbException) { - Toast.makeText(this, - R.string.reading_contacts_error, - Toast.LENGTH_LONG).show(); - finish(); - } - CustodianSelectorFragment fragment = - CustodianSelectorFragment.newInstance(); - 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) { - 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..7b3cd6300 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 @@ -7,6 +7,7 @@ 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; @@ -16,8 +17,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,19 +29,16 @@ 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) { - Bundle bundle = new Bundle(); - List custodians = backupMetadata.getCustodians(); - ArrayList custodianNames = new ArrayList(); - for (Author custodian : custodians) { - custodianNames.add(custodian.getName()); - } - bundle.putStringArrayList(CUSTODIANS, custodianNames); - bundle.putInt(THRESHOLD, backupMetadata.getThreshold()); - ExistingBackupFragment fragment = new ExistingBackupFragment(); - fragment.setArguments(bundle); - return fragment; + @Inject + ViewModelProvider.Factory viewModelFactory; + + private SocialBackupSetupViewModel viewModel; + + @Override + public void injectFragment(ActivityComponent component) { + component.inject(this); + viewModel = new ViewModelProvider(requireActivity(), viewModelFactory) + .get(SocialBackupSetupViewModel.class); } @Override @@ -53,20 +54,20 @@ public class ExistingBackupFragment extends BaseFragment { ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_existing_backup, container, false); - Bundle args = requireArguments(); - ArrayList custodianNames = args.getStringArrayList(CUSTODIANS); + BackupMetadata backupMetadata = viewModel.getBackupMetadata(); + List custodians = backupMetadata.getCustodians(); StringBuilder custodianNamesString = new StringBuilder(); - for (String custodianName : custodianNames) { + for (Author custodian : custodians) { custodianNamesString .append("• ") - .append(custodianName) + .append(custodian.getName()) .append("\n"); } TextView textViewThreshold = view.findViewById(R.id.textViewThreshold); textViewThreshold.setText(getString(R.string.existing_backup_explain, - args.getInt(THRESHOLD))); + backupMetadata.getThreshold())); TextView textViewCustodians = view.findViewById(R.id.textViewCustodians); textViewCustodians.setText(custodianNamesString); @@ -79,15 +80,9 @@ public class ExistingBackupFragment extends BaseFragment { // 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/recover/CustodianReturnShardSuccessFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/SetupExplainerFragment.java similarity index 53% rename from briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardSuccessFragment.java rename to briar-android/src/main/java/org/briarproject/briar/android/socialbackup/SetupExplainerFragment.java index b058d1bb7..a5ab84a8b 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardSuccessFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/SetupExplainerFragment.java @@ -1,4 +1,4 @@ -package org.briarproject.briar.android.socialbackup.recover; +package org.briarproject.briar.android.socialbackup; import android.os.Bundle; import android.view.LayoutInflater; @@ -6,8 +6,6 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Button; -import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; -import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.fragment.BaseFragment; @@ -18,37 +16,33 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.lifecycle.ViewModelProvider; -@MethodsNotNullByDefault -@ParametersNotNullByDefault -public class CustodianReturnShardSuccessFragment extends - BaseFragment { +public class SetupExplainerFragment extends BaseFragment { public static final String TAG = - CustodianReturnShardFragment.class.getName(); + SetupExplainerFragment.class.getName(); @Inject ViewModelProvider.Factory viewModelFactory; - private CustodianReturnShardViewModel viewModel; + private SocialBackupSetupViewModel viewModel; @Override public void injectFragment(ActivityComponent component) { component.inject(this); viewModel = new ViewModelProvider(requireActivity(), viewModelFactory) - .get(CustodianReturnShardViewModel.class); + .get(SocialBackupSetupViewModel.class); } @Nullable @Override - public View onCreateView(@NonNull LayoutInflater inflater, - @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_recovery_custodian_done, - container, false); + 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.onSuccessDismissed()); - + button.setOnClickListener(e -> viewModel.onExplainerDismissed()); return view; } @@ -57,3 +51,4 @@ public class CustodianReturnShardSuccessFragment extends return TAG; } } + 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 deleted file mode 100644 index c6ad906d6..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ShardsSentFragment.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.briarproject.briar.android.socialbackup; - -import android.content.Context; -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 androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.UiThread; - -public class ShardsSentFragment extends BaseFragment { - - public static final String TAG = ShardsSentFragment.class.getName(); - - interface ShardsSentDismissedListener { - - @UiThread - void shardsSentDismissed(); - - } - - 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); - } - - public void onBackPressed() { - listener.shardsSentDismissed(); - } - -} 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 new file mode 100644 index 000000000..0c616cb88 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/SocialBackupSetupActivity.java @@ -0,0 +1,93 @@ +package org.briarproject.briar.android.socialbackup; + +import android.os.Bundle; +import android.widget.Toast; + +import org.briarproject.bramble.api.contact.ContactId; +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 java.util.Collection; + +import javax.inject.Inject; + +import androidx.lifecycle.ViewModelProvider; + +public class SocialBackupSetupActivity extends BriarActivity implements + BaseFragment.BaseFragmentListener, ContactSelectorListener { + + private SocialBackupSetupViewModel viewModel; + + @Inject + ViewModelProvider.Factory viewModelFactory; + + @Override + public void injectActivity(ActivityComponent component) { + component.inject(this); + viewModel = new ViewModelProvider(this, viewModelFactory) + .get(SocialBackupSetupViewModel.class); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_distributed_backup); + + if (viewModel.haveExistingBackup()) { + showInitialFragment(new ExistingBackupFragment()); + } else { + try { + if (!viewModel.haveEnoughContacts()) { + Toast.makeText(this, + R.string.social_backup_not_enough_contacts, + Toast.LENGTH_LONG).show(); + finish(); + } + } catch (DbException dbException) { + Toast.makeText(this, + R.string.reading_contacts_error, + Toast.LENGTH_LONG).show(); + finish(); + } + showInitialFragment(new SetupExplainerFragment()); + } + + viewModel.getState() + .observe(this, this::onStateChanged); + } + + private void onStateChanged(SocialBackupSetupViewModel.State state) { + switch (state) { + case SUCCESS: + finish(); + break; + case FAILURE: + 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; + } + } + + @Override + public void contactsSelected(Collection contacts) { + Toast.makeText(this, + String.format("Selected %d contacts", contacts.size()), + Toast.LENGTH_SHORT).show(); + viewModel.setCustodians(contacts); + ThresholdSelectorFragment fragment = + ThresholdSelectorFragment.newInstance(contacts.size()); + showNextFragment(fragment); + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/SocialBackupSetupModule.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/SocialBackupSetupModule.java new file mode 100644 index 000000000..7d7f8f7b2 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/SocialBackupSetupModule.java @@ -0,0 +1,20 @@ +package org.briarproject.briar.android.socialbackup; + +import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardViewModel; +import org.briarproject.briar.android.viewmodel.ViewModelKey; + +import androidx.lifecycle.ViewModel; +import dagger.Binds; +import dagger.Module; +import dagger.multibindings.IntoMap; + +@Module +public abstract class SocialBackupSetupModule { + + @Binds + @IntoMap + @ViewModelKey(SocialBackupSetupViewModel.class) + abstract ViewModel bindSocialBackupSetupViewModel( + SocialBackupSetupViewModel socialBackupSetupViewModel); + +} 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..1b80dfa2e --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/SocialBackupSetupViewModel.java @@ -0,0 +1,106 @@ +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, + FAILURE + } + + 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 { + backupMetadata = db.transactionWithNullableResult(true, + socialBackupManager::getBackupMetadata); + } catch (DbException e) { + return false; + } + return (backupMetadata != null); + } + + 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() { + try { + db.transaction(false, txn -> { + socialBackupManager + .createBackup(txn, (List) custodians, + threshold); + }); + } catch (DbException e) { + state.postValue(State.FAILURE); + } + } + + public void setThreshold(int threshold) { + this.threshold = threshold; + createBackup(); + } + + public void onSuccessDismissed() { + 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/ThresholdDefinedListener.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ThresholdDefinedListener.java deleted file mode 100644 index ee99b999a..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/ThresholdDefinedListener.java +++ /dev/null @@ -1,12 +0,0 @@ -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 index c4b689f39..716a448e9 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 @@ -2,6 +2,10 @@ package org.briarproject.briar.android.socialbackup; import android.content.Context; import android.os.Bundle; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.style.ImageSpan; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -11,22 +15,25 @@ import android.view.ViewGroup; import android.widget.SeekBar; import android.widget.TextView; -import org.briarproject.bramble.api.db.DbException; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.fragment.BaseFragment; import org.magmacollective.darkcrystal.secretsharingwrapper.SecretSharingWrapper; +import java.util.Arrays; + +import javax.inject.Inject; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.lifecycle.ViewModelProvider; public class ThresholdSelectorFragment extends BaseFragment { public static final String TAG = ThresholdSelectorFragment.class.getName(); private static final String NUMBER_CUSTODIANS = "numberCustodians"; - protected ThresholdDefinedListener listener; - private int numberOfCustodians; private int threshold; private int recommendedThreshold; @@ -35,6 +42,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); @@ -94,7 +113,6 @@ public class ThresholdSelectorFragment extends BaseFragment { @Override public void onAttach(Context context) { super.onAttach(context); - listener = (ThresholdDefinedListener) context; } @@ -103,11 +121,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,26 +131,42 @@ 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); + showSuccessDialog(); 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); + private void showSuccessDialog() { + AlertDialog.Builder builder = new AlertDialog.Builder(requireContext(), + R.style.BriarDialogTheme); + builder.setTitle(R.string.backup_created); + builder.setMessage(R.string.backup_done_info); + builder.setPositiveButton(R.string.ok, + (dialog, which) -> viewModel.onSuccessDismissed()); + builder.setIcon(R.drawable.ic_baseline_done_outline_24); + AlertDialog dialog = builder.create(); + dialog.show(); + } + + private SpannableStringBuilder buildThresholdRepresentationString() { + 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); } - for (int i = 0; i < (numberOfCustodians - threshold); i++) { - thresholdRepresentationText += getString(R.string.linear_bullet); - } - return thresholdRepresentationText; + // If we have more than 6, split it on two lines + if (numberOfCustodians > 6) string.insert(4, "\n"); + return string; } private class SeekBarListener implements SeekBar.OnSeekBarChangeListener { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianRecoveryModeExplainerFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianRecoveryModeExplainerFragment.java index 49df13ebf..9cd889fd4 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianRecoveryModeExplainerFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianRecoveryModeExplainerFragment.java @@ -32,11 +32,6 @@ public class CustodianRecoveryModeExplainerFragment extends BaseFragment { viewModel = new ViewModelProvider(requireActivity(), viewModelFactory) .get(CustodianReturnShardViewModel.class); } -// @Override -// public void onCreate(@Nullable Bundle savedInstanceState) { -// super.onCreate(savedInstanceState); -// requireActivity().setTitle(R.string.title_help_recover); -// } @Nullable @Override diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardActivity.java index 976ed8581..93315f914 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardActivity.java @@ -13,7 +13,6 @@ import org.briarproject.briar.android.fragment.BaseFragment; import org.briarproject.briar.api.socialbackup.recovery.CustodianTask; import java.io.IOException; -import java.util.logging.Logger; import javax.annotation.Nullable; import javax.inject.Inject; @@ -21,17 +20,12 @@ import javax.inject.Inject; import androidx.fragment.app.FragmentManager; import androidx.lifecycle.ViewModelProvider; -import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; -import static java.util.logging.Logger.getLogger; import static org.briarproject.briar.android.conversation.ConversationActivity.CONTACT_ID; public class CustodianReturnShardActivity extends BriarActivity implements BaseFragment.BaseFragmentListener { private CustodianReturnShardViewModel viewModel; - private static final Logger LOG = - getLogger(CustodianReturnShardActivity.class.getName()); - private ContactId contactId; @Inject ViewModelProvider.Factory viewModelFactory; @@ -52,7 +46,7 @@ public class CustodianReturnShardActivity extends BriarActivity Intent intent = getIntent(); int id = intent.getIntExtra(CONTACT_ID, -1); if (id == -1) throw new IllegalStateException("No ContactId"); - contactId = new ContactId(id); + ContactId contactId = new ContactId(id); try { viewModel.start(contactId); @@ -96,7 +90,8 @@ public class CustodianReturnShardActivity extends BriarActivity "It looks like you are not connected to a Wifi network", Toast.LENGTH_SHORT).show(); FragmentManager fm = getSupportFragmentManager(); - if (fm.findFragmentByTag(CustodianReturnShardFragment.TAG) == null) { + if (fm.findFragmentByTag(CustodianReturnShardFragment.TAG) == + null) { BaseFragment f = new CustodianReturnShardErrorFragment(); fm.beginTransaction() .replace(R.id.fragmentContainer, f, f.getUniqueTag()) @@ -106,19 +101,15 @@ public class CustodianReturnShardActivity extends BriarActivity } } - private void onReturnShardStateChanged(CustodianTask.State state) { - if (state instanceof CustodianTask.State.Success) { - CustodianReturnShardSuccessFragment fragment = new CustodianReturnShardSuccessFragment(); - showNextFragment(fragment); - } else if (state instanceof CustodianTask.State.Failure) { - // TODO error fragment here - // TODO handle reason - Toast.makeText(this, - "Backup piece transfer failed", - Toast.LENGTH_SHORT).show(); - finish(); - } + if (state instanceof CustodianTask.State.Failure) { + // TODO error fragment here + // TODO handle reason + Toast.makeText(this, + "Backup piece transfer failed", + Toast.LENGTH_SHORT).show(); + finish(); + } } private void showCameraFragment() { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardFragment.java index d893b5b0b..5c64cb44a 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardFragment.java @@ -5,6 +5,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; +import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; @@ -24,6 +25,7 @@ import javax.annotation.Nullable; import javax.inject.Inject; import androidx.annotation.UiThread; +import androidx.appcompat.app.AlertDialog; import androidx.lifecycle.ViewModelProvider; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; @@ -53,6 +55,7 @@ public class CustodianReturnShardFragment extends BaseFragment private LinearLayout cameraOverlay; private View statusView; private TextView status; + private ProgressBar bottomSpinner; public static CustodianReturnShardFragment newInstance() { Bundle args = new Bundle(); @@ -84,6 +87,7 @@ public class CustodianReturnShardFragment extends BaseFragment cameraOverlay = view.findViewById(R.id.camera_overlay); statusView = view.findViewById(R.id.status_container); status = view.findViewById(R.id.connect_status); + bottomSpinner = view.findViewById(R.id.qr_code_progress_bar); viewModel.getState().observe(getViewLifecycleOwner(), this::onReturnShardStateChanged); @@ -141,7 +145,6 @@ public class CustodianReturnShardFragment extends BaseFragment @UiThread private void onReturnShardStateChanged(@Nullable CustodianTask.State state) { - LOG.info("State changed"); // if (state instanceof CustodianTask.State.Connecting) { // try { // cameraView.stop(); @@ -163,8 +166,9 @@ public class CustodianReturnShardFragment extends BaseFragment } else if (state instanceof CustodianTask.State.ReceivingAck) { status.setText("Receiving Ack"); } else if (state instanceof CustodianTask.State.Success) { - // TODO - status.setText(R.string.exchanging_contact_details); + statusView.setVisibility(INVISIBLE); + bottomSpinner.setVisibility(INVISIBLE); + showSuccessDialog(); } else if (state instanceof CustodianTask.State.Failure) { // the activity will replace this fragment with an error fragment statusView.setVisibility(INVISIBLE); @@ -190,4 +194,15 @@ public class CustodianReturnShardFragment extends BaseFragment requireActivity().getSupportFragmentManager().popBackStack(); } + private void showSuccessDialog() { + AlertDialog.Builder builder = new AlertDialog.Builder(requireContext(), + R.style.BriarDialogTheme); + builder.setTitle(R.string.custodian_shard_sent); + //builder.setMessage(); + builder.setPositiveButton(R.string.ok, + (dialog, which) -> viewModel.onSuccessDismissed()); + builder.setIcon(R.drawable.ic_baseline_done_outline_24); + AlertDialog dialog = builder.create(); + dialog.show(); + } } 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..7a23c78ff 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 = @@ -177,7 +177,6 @@ public class CustodianReturnShardViewModel extends AndroidViewModel successDismissed.setEvent(true); } - QrCodeDecoder getQrCodeDecoder() { return qrCodeDecoder; } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardActivity.java index 3ea98fcc4..fc38b9a2a 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardActivity.java @@ -44,13 +44,6 @@ public class OwnerReturnShardActivity extends BaseActivity private OwnerReturnShardViewModel viewModel; -// private final ActivityResultLauncher permissionLauncher = -// registerForActivityResult( -// new ActivityResultContracts.RequestMultiplePermissions(), -// r -> -// permissionManager.onRequestPermissionResult(r, -// viewModel::showQrCodeFragmentIfAllowed)); - @Override public void injectActivity(ActivityComponent component) { component.inject(this); @@ -154,7 +147,8 @@ public class OwnerReturnShardActivity extends BaseActivity "WARNING: Mismatched backup piece!", Toast.LENGTH_LONG).show(); } - boolean added = (result != RestoreAccount.AddReturnShardPayloadResult.DUPLICATE) ? true : false; + boolean added = result != + RestoreAccount.AddReturnShardPayloadResult.DUPLICATE; Toast.makeText(this, "Success - got backup piece" + (added ? "" : " duplicate"), Toast.LENGTH_SHORT).show(); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardFragment.java index d66043907..b2bc2297d 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardFragment.java @@ -27,7 +27,6 @@ import androidx.lifecycle.ViewModelProvider; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.view.View.INVISIBLE; -import static android.view.View.VISIBLE; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.widget.LinearLayout.HORIZONTAL; @@ -158,5 +157,4 @@ public class OwnerReturnShardFragment extends BaseFragment protected void finish() { requireActivity().getSupportFragmentManager().popBackStack(); } - } diff --git a/briar-android/src/main/res/drawable/button_positive_tiny_disable.xml b/briar-android/src/main/res/drawable/button_positive_tiny_disable.xml new file mode 100644 index 000000000..a39d5afef --- /dev/null +++ b/briar-android/src/main/res/drawable/button_positive_tiny_disable.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/briar-android/src/main/res/drawable/ic_custodian_optional.xml b/briar-android/src/main/res/drawable/ic_custodian_optional.xml new file mode 100644 index 000000000..12e93057b --- /dev/null +++ b/briar-android/src/main/res/drawable/ic_custodian_optional.xml @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/briar-android/src/main/res/drawable/ic_custodian_required.xml b/briar-android/src/main/res/drawable/ic_custodian_required.xml new file mode 100644 index 000000000..65c59ea9f --- /dev/null +++ b/briar-android/src/main/res/drawable/ic_custodian_required.xml @@ -0,0 +1,5 @@ + + + diff --git a/briar-android/src/main/res/drawable/ic_social_backup_group.xml b/briar-android/src/main/res/drawable/ic_social_backup_group.xml new file mode 100644 index 000000000..b056737ab --- /dev/null +++ b/briar-android/src/main/res/drawable/ic_social_backup_group.xml @@ -0,0 +1,7 @@ + + + \ 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 index 293518b6b..fa59fb005 100644 --- a/briar-android/src/main/res/layout/activity_preview_welcome.xml +++ b/briar-android/src/main/res/layout/activity_preview_welcome.xml @@ -16,6 +16,7 @@ app:title="@string/setup_title" app:titleTextColor="@android:color/white" /> - + + \ No newline at end of file diff --git a/briar-android/src/main/res/layout/fragment_new_or_recover.xml b/briar-android/src/main/res/layout/fragment_new_or_recover.xml index ad5ec624b..5a2bf34f1 100644 --- a/briar-android/src/main/res/layout/fragment_new_or_recover.xml +++ b/briar-android/src/main/res/layout/fragment_new_or_recover.xml @@ -13,22 +13,38 @@