Compare commits

...

30 Commits

Author SHA1 Message Date
ameba23
7df10d9324 Improve existing backup fragment 2022-03-04 10:09:57 +01:00
ameba23
b7bdad4b67 Add method to get contact ids of existing custodians 2022-03-03 10:48:23 +01:00
ameba23
c0932b4df1 Improve existing backup fragment 2022-03-03 10:47:31 +01:00
ameba23
c49ea5173e Improve strings for threshold selector screen 2022-03-01 09:30:06 +01:00
ameba23
7a9794e62a Improve setup password explanation string 2022-02-21 08:25:48 +01:00
ameba23
8ad7ec3a66 Improve choose password info dialog 2022-02-18 09:00:21 +01:00
ameba23
32f1676e55 Improve threshold selector screen 2022-02-15 08:44:36 +01:00
ameba23
ae7a553aa3 Basic strength meter when choosing threshold 2022-02-11 12:02:39 +01:00
ameba23
2c235947b2 Layout of social backup setup explainer 2022-02-10 10:37:27 +01:00
ameba23
c7809add20 Improve social backup setup explainer screen, and add image 2022-02-10 10:18:39 +01:00
ameba23
09823c4402 Add material tap target prompt for social backup feature 2022-02-10 10:03:58 +01:00
ameba23
33cbf2218e Improve social backup setup explainer screen 2022-02-09 12:19:10 +01:00
ameba23
1edfdaeaf0 Remove original custodian return shard success fragment 2022-02-09 11:57:06 +01:00
ameba23
456fd7d2e4 Popup dialog on shards sent for custodian 2022-02-09 11:40:18 +01:00
ameba23
3e7f3fcf5d If we have more than 6 custodians, display on 2 lines 2022-02-08 15:25:49 +01:00
ameba23
62988e7769 Set maximum amount of custodians in UI 2022-02-08 15:06:50 +01:00
ameba23
386658735c Increase size of custodian icons 2022-02-08 11:42:01 +01:00
ameba23
2f2edc3161 Improve visual feedback for threshold selector 2022-02-08 11:19:52 +01:00
ameba23
705de81429 Delete old shards sent fragment 2022-02-08 09:54:38 +01:00
ameba23
d359b1fb9c Popup dialog on shards sent 2022-02-08 09:50:21 +01:00
ameba23
b9867ddc4b Fix styling on disabled recover button 2022-02-07 11:22:17 +01:00
ameba23
67febd4e57 Disable recover button when name is entered 2022-02-04 11:56:16 +01:00
ameba23
c59504b38f Move recover button to author name fragment 2022-02-04 11:40:08 +01:00
ameba23
5b63aa28d3 Fix explainer fragment 2022-02-04 09:08:13 +01:00
ameba23
c9140195cc Rename social backup setup activity 2022-02-04 09:01:53 +01:00
ameba23
5a116f08c5 Add string for explainer button 2022-02-02 11:17:07 +01:00
ameba23
daf233ccd7 Add module, tidy 2022-02-02 11:12:56 +01:00
ameba23
43e8d06493 Add view model for setup process, add setup explainer fragment 2022-02-02 10:30:50 +01:00
ameba23
01fd193686 Fix setting default threshold slider position 2021-10-06 10:26:32 +02:00
ameba23
f1b8edd8cf Rm danger of loss message with 2 custodians 2021-10-06 10:19:25 +02:00
44 changed files with 763 additions and 612 deletions

View File

@@ -139,16 +139,7 @@
</activity>
<activity
android:name="org.briarproject.briar.android.account.NewOrRecoverActivity"
android:label="@string/activity_name_new_or_recover_account"
android:parentActivityName="org.briarproject.briar.android.login.StartupActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.login.StartupActivity" />
</activity>
<activity
android:name="org.briarproject.briar.android.socialbackup.DistributedBackupActivity"
android:name="org.briarproject.briar.android.socialbackup.SocialBackupSetupActivity"
android:label="@string/activity_name_distributed_backup"
android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity">
<meta-data
@@ -168,10 +159,11 @@
<activity
android:name="org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardActivity"
android:label="@string/activity_name_recovery"
android:parentActivityName="org.briarproject.briar.android.account.NewOrRecoverActivity">
android:parentActivityName="org.briarproject.briar.android.account.SetupActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.account.NewOrRecoverActivity" />
android:value="org.briarproject.briar.android.account.SetupActivity" />
</activity>
<activity

View File

@@ -42,6 +42,7 @@ import org.briarproject.briar.android.privategroup.list.GroupListModule;
import org.briarproject.briar.android.reporting.DevReportModule;
import org.briarproject.briar.android.settings.SettingsModule;
import org.briarproject.briar.android.sharing.SharingModule;
import org.briarproject.briar.android.socialbackup.SocialBackupSetupModule;
import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardModule;
import org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardModule;
import org.briarproject.briar.android.test.TestAvatarCreatorImpl;
@@ -94,7 +95,8 @@ import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
GroupConversationModule.class,
SharingModule.class,
OwnerReturnShardModule.class,
CustodianReturnShardModule.class
CustodianReturnShardModule.class,
SocialBackupSetupModule.class
})
public class AppModule {

View File

@@ -30,6 +30,7 @@ public class AuthorNameFragment extends SetupFragment {
private TextInputLayout authorNameWrapper;
private TextInputEditText authorNameInput;
private Button nextButton;
private Button recoverButton;
public static AuthorNameFragment newInstance() {
return new AuthorNameFragment();
@@ -54,6 +55,9 @@ public class AuthorNameFragment extends SetupFragment {
authorNameInput.addTextChangedListener(this);
nextButton.setOnClickListener(this);
recoverButton = v.findViewById(R.id.buttonRestoreAccount);
recoverButton.setOnClickListener(e -> 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

View File

@@ -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.");
}
}

View File

@@ -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);
}
}

View File

@@ -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) {

View File

@@ -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);
}
}

View File

@@ -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;
@@ -80,20 +78,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;
@@ -102,7 +99,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;
@@ -205,8 +201,6 @@ public interface ActivityComponent {
void inject(CrashReportActivity crashReportActivity);
void inject(NewOrRecoverActivity newOrRecoverActivity);
void inject(CustodianReturnShardActivity custodianReturnShardActivity);
void inject(OwnerReturnShardActivity ownerReturnShardActivity);
@@ -275,28 +269,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);
@@ -306,4 +294,6 @@ public interface ActivityComponent {
void inject(OwnerRecoveryModeErrorFragment ownerRecoveryModeErrorFragment);
void inject(CustodianReturnShardErrorFragment custodianReturnShardErrorFragment);
void inject(SetupExplainerFragment setupExplainerFragment);
}

View File

@@ -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);

View File

@@ -158,7 +158,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);
@@ -167,9 +168,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);
@@ -468,6 +472,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;

View File

@@ -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<Boolean> showTransportsOnboarding =
new MutableLiveData<>();
private final MutableLiveData<Boolean> showSocialBackupOnboarding =
new MutableLiveData<>();
@Inject
NavDrawerViewModel(Application app,
@@ -142,6 +146,11 @@ public class NavDrawerViewModel extends DbViewModel {
return showTransportsOnboarding;
}
@UiThread
LiveData<Boolean> 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);
}
});
}
}

View File

@@ -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 {

View File

@@ -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();
}
}

View File

@@ -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<ContactId> 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<ContactId> 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<ContactId>) 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();
}
}

View File

@@ -2,42 +2,48 @@ 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.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<ContactListItem> {
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;
public static ExistingBackupFragment newInstance(
BackupMetadata backupMetadata) {
Bundle bundle = new Bundle();
List<Author> 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
@@ -51,43 +57,69 @@ 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);
Bundle args = requireArguments();
ArrayList<String> custodianNames = args.getStringArrayList(CUSTODIANS);
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 (String custodianName : custodianNames) {
custodianNamesString
.append("")
.append(custodianName)
.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,
args.getInt(THRESHOLD)));
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.existing_backup_explain), threshold));
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
public String getUniqueTag() {
return TAG;
}
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,94 @@
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 java.util.List;
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<ContactId> contacts) {
Toast.makeText(this,
String.format("Selected %d contacts", contacts.size()),
Toast.LENGTH_SHORT).show();
viewModel.setCustodians((List<ContactId>) contacts);
ThresholdSelectorFragment fragment =
ThresholdSelectorFragment.newInstance(contacts.size());
showNextFragment(fragment);
}
}

View File

@@ -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);
}

View File

@@ -0,0 +1,147 @@
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.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import javax.inject.Inject;
import androidx.annotation.NonNull;
import androidx.lifecycle.MutableLiveData;
public class SocialBackupSetupViewModel extends ContactsViewModel {
private final SocialBackupManager socialBackupManager;
private final DatabaseComponent db;
private final ContactManager contactManager;
private BackupMetadata backupMetadata;
private List<ContactId> custodians;
private int threshold;
public enum State {
EXPLAINING,
CHOOSING_CUSTODIANS,
GETTING_THRESHOLD,
SUCCESS,
FAILURE
}
private final MutableLiveData<State> 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, 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,
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(List<ContactId> contacts) {
custodians = contacts;
}
public void createBackup() {
try {
db.transaction(false, txn -> {
socialBackupManager
.createBackup(txn, (List<ContactId>) 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<State> getState() {
return state;
}
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();
}
}

View File

@@ -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;
}

View File

@@ -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,28 @@ 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.briarproject.briar.android.login.StrengthMeter;
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;
import static android.view.View.GONE;
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;
@@ -34,6 +44,19 @@ public class ThresholdSelectorFragment extends BaseFragment {
private TextView thresholdRepresentation;
private TextView message;
private TextView mOfn;
private StrengthMeter strengthMeter;
@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();
@@ -62,21 +85,21 @@ public class ThresholdSelectorFragment extends BaseFragment {
thresholdRepresentation =
view.findViewById(R.id.textViewThresholdRepresentation);
message = view.findViewById(R.id.textViewMessage);
mOfn = view.findViewById(R.id.textViewmOfn);
mOfn = view.findViewById(R.id.text_view_m_of_n);
strengthMeter = view.findViewById(R.id.strength_meter);
if (numberOfCustodians == 2) {
message.setText(R.string.threshold_too_few_custodians);
}
if (numberOfCustodians > 3) {
seekBar.setMax(numberOfCustodians -3);
seekBar.setProgress(threshold - 2);
seekBar.setOnSeekBarChangeListener(new SeekBarListener());
recommendedThreshold =
SecretSharingWrapper.defaultThreshold(numberOfCustodians);
threshold = recommendedThreshold;
seekBar.setProgress(threshold - 2);
strengthMeter.setStrength(1);
} else {
seekBar.setEnabled(false);
seekBar.setVisibility(GONE);
strengthMeter.setVisibility(GONE);
threshold = 2;
seekBar.setMax(numberOfCustodians);
seekBar.setProgress(threshold);
@@ -98,20 +121,13 @@ public class ThresholdSelectorFragment extends BaseFragment {
@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);
@@ -122,26 +138,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 {
@@ -159,12 +191,23 @@ public class ThresholdSelectorFragment extends BaseFragment {
int sanityLevel = SecretSharingWrapper
.thresholdSanity(threshold, numberOfCustodians);
int text = R.string.threshold_secure;
if (threshold == recommendedThreshold)
float strength = 1;
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;
}
if (sanityLevel < -1) {
strength = 0.75f;
text = R.string.threshold_low_insecure;
}
if (sanityLevel < -2) {
strength = 0.5f;
}
if (sanityLevel > 0) {
strength = 0.75f;
text = R.string.threshold_high_insecure;
}
strengthMeter.setStrength(strength);
message.setText(text);
// TODO change colour of thresholdRepresentation to green/red based on sanityLevel
}
@Override

View File

@@ -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

View File

@@ -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() {

View File

@@ -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();
}
}

View File

@@ -53,7 +53,7 @@ public class CustodianReturnShardViewModel extends AndroidViewModel
final QrCodeDecoder qrCodeDecoder;
private boolean qrCodeRead = false;
private WifiManager wifiManager;
private final MutableLiveEvent<Boolean > continueClicked = new MutableLiveEvent<>();
private final MutableLiveEvent<Boolean> continueClicked = new MutableLiveEvent<>();
private final MutableLiveEvent<Boolean> showCameraFragment =
new MutableLiveEvent<>();
private final MutableLiveEvent<Boolean> successDismissed =
@@ -177,7 +177,6 @@ public class CustodianReturnShardViewModel extends AndroidViewModel
successDismissed.setEvent(true);
}
QrCodeDecoder getQrCodeDecoder() {
return qrCodeDecoder;
}

View File

@@ -44,13 +44,6 @@ public class OwnerReturnShardActivity extends BaseActivity
private OwnerReturnShardViewModel viewModel;
// private final ActivityResultLauncher<String[]> 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();

View File

@@ -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();
}
}

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/briar_button_text_positive" android:state_enabled="true"/>
<item android:color="@color/briar_button_text_disabled" />
</selector>

View File

@@ -0,0 +1,5 @@
<vector android:height="48dp" android:tint="#660066"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector android:height="48dp" android:tint="#FF00FF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
</vector>

View File

@@ -0,0 +1,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="72dp"
android:width="72dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M12,5.5A3.5,3.5 0 0,1 15.5,9A3.5,3.5 0 0,1 12,12.5A3.5,3.5 0 0,1 8.5,9A3.5,3.5 0 0,1 12,5.5M5,8C5.56,8 6.08,8.15 6.53,8.42C6.38,9.85 6.8,11.27 7.66,12.38C7.16,13.34 6.16,14 5,14A3,3 0 0,1 2,11A3,3 0 0,1 5,8M19,8A3,3 0 0,1 22,11A3,3 0 0,1 19,14C17.84,14 16.84,13.34 16.34,12.38C17.2,11.27 17.62,9.85 17.47,8.42C17.92,8.15 18.44,8 19,8M5.5,18.25C5.5,16.18 8.41,14.5 12,14.5C15.59,14.5 18.5,16.18 18.5,18.25V20H5.5V18.25M0,20V18.5C0,17.11 1.89,15.94 4.45,15.6C3.86,16.28 3.5,17.22 3.5,18.25V20H0M24,20H20.5V18.25C20.5,17.22 20.14,16.28 19.55,15.6C22.11,15.94 24,17.11 24,18.5V20Z" />
</vector>

View File

@@ -16,6 +16,7 @@
app:title="@string/setup_title"
app:titleTextColor="@android:color/white" />
<include layout="@layout/fragment_start" />
<!-- <include layout="@layout/fragment_start" />-->
<include layout="@layout/fragment_new_or_recover" />
</LinearLayout>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
@@ -11,25 +10,35 @@
android:paddingBottom="@dimen/margin_medium"
android:layout_height="match_parent">
<TextView
android:id="@+id/textViewThreshold"
android:id="@+id/textViewThresholdRepresentation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="placeholder threshold"
android:textSize="16sp"
android:textSize="64sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textViewCustodians"
android:id="@+id/textViewThreshold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/backup_done_info"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewThreshold" />
app:layout_constraintTop_toBottomOf="@+id/textViewThresholdRepresentation" />
<org.briarproject.briar.android.view.BriarRecyclerView
android:id="@+id/custodianList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textViewThreshold"
app:scrollToEnd="false" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -13,22 +13,38 @@
<Button
android:id="@+id/buttonSetupNewAccount"
style="@style/BriarButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_contacts"
android:text="@string/setup_new_account"
android:drawableLeft="@drawable/ic_contacts"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent"
android:drawableStart="@drawable/ic_contacts" />
<TextView
android:id="@+id/textViewExplain"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:gravity="center"
android:text="@string/custodian_recovery_explainer_extra"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/buttonSetupNewAccount"
tools:layout_editor_absoluteX="16dp" />
<Button
android:id="@+id/buttonRestoreAccount"
android:id="@+id/buttonRestoreAccount2"
style="@style/BriarButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_repeat"
android:text="@string/setup_restore_account"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/buttonSetupNewAccount" />
app:layout_constraintTop_toBottomOf="@+id/textViewExplain"
tools:enabled="true"
android:drawableStart="@drawable/ic_repeat" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -45,7 +45,7 @@
app:layout_constraintTop_toBottomOf="@+id/seekBar" />
<TextView
android:id="@+id/textViewmOfn"
android:id="@+id/text_view_m_of_n"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
@@ -55,6 +55,17 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewThresholdRepresentation" />
<org.briarproject.briar.android.login.StrengthMeter
android:id="@+id/strength_meter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:visibility="visible"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_view_m_of_n"
tools:visibility="visible" />
<TextView
android:id="@+id/textViewMessage"
android:layout_width="wrap_content"
@@ -64,6 +75,6 @@
android:textSize="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewmOfn" />
app:layout_constraintTop_toBottomOf="@+id/strength_meter" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -47,14 +47,28 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_activity_horizontal"
android:enabled="false"
android:text="@string/setup_next"
app:layout_constraintBottom_toBottomOf="parent"
android:text="@string/setup_new_account"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/nickname_entry_wrapper"
app:layout_constraintVertical_bias="1.0"
tools:enabled="true" />
<Button
android:id="@+id/buttonRestoreAccount"
style="@style/BriarButtonFlat.Positive.Tiny"
android:textColor="@drawable/button_positive_tiny_disable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_activity_horizontal"
android:text="@string/setup_restore_account"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/next"
app:layout_constraintVertical_bias="1.0"
tools:enabled="true" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View File

@@ -1,50 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/margin_large"
android:paddingTop="@dimen/margin_medium"
android:paddingRight="@dimen/margin_large"
android:paddingBottom="@dimen/margin_medium">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="@string/ok"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView2" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/backup_done_info"
android:gravity="center"
android:textSize="30sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView2" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginStart="32dp"
android:layout_marginLeft="32dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="32dp"
android:layout_marginRight="32dp"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_baseline_done_outline_24" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -8,43 +8,52 @@
android:paddingLeft="@dimen/margin_large"
android:paddingTop="@dimen/margin_medium"
android:paddingRight="@dimen/margin_large"
android:paddingBottom="@dimen/margin_medium">
android:paddingBottom="@dimen/margin_medium"
tools:ignore="VectorDrawableCompat">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:gravity="center"
android:text="@string/social_backup_setup_explainer_title"
android:textSize="24sp"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteX="16dp" />
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView"
app:srcCompat="@drawable/ic_social_backup_group" />
<TextView
android:id="@+id/textViewExplain"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:gravity="center"
android:text="@string/social_backup_setup_explainer_long"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView"
tools:layout_editor_absoluteX="16dp" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="@string/ok"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView2" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:text="@string/custodian_shard_sent"
android:textSize="30sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView2" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginStart="32dp"
android:layout_marginLeft="32dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="32dp"
android:layout_marginRight="32dp"
app:layout_constraintDimensionRatio="1:1"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="@string/social_backup_setup_explainer_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_baseline_done_outline_24" />
app:layout_constraintTop_toBottomOf="@+id/textViewExplain" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -6,6 +6,7 @@
<ProgressBar
style="?android:attr/progressBarStyleLarge"
android:id="@+id/qr_code_progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />

View File

@@ -9,7 +9,7 @@
<string name="setup_name_explanation">Your nickname will be shown next to any content you post. You can\'t change it after creating your account.</string>
<string name="setup_next">Next</string>
<string name="setup_password_intro">Choose a Password</string>
<string name="setup_password_explanation">Your Briar account is stored encrypted on your device, not in the cloud. If you forget your password or uninstall Briar, you can only recover your account if you have made a social backup.\n\nChoose a long password that\'s hard to guess, such as four random words, or ten random letters, numbers and symbols.</string>
<string name="setup_password_explanation">Your Briar account is stored encrypted on your device, not in the cloud. If you forget your password or uninstall Briar, your account will be lost.\n\nTo avoid this, use the \'Social Backup\' feature in settings menu to make a backup.</string>
<string name="setup_doze_title">Background Connections</string>
<string name="setup_doze_intro">To receive messages, Briar needs to stay connected in the background.</string>
<string name="setup_doze_explanation">To receive messages, Briar needs to stay connected in the background. Please disable battery optimizations so Briar can stay connected.</string>
@@ -653,24 +653,30 @@
<!-- social backup procedure -->
<string name="select_custodians">Select Trusted Contacts:</string>
<string name="select_custodians">Select Trusted Contacts</string>
<string name="select_at_least_n_contacts">Please select at least %d contacts</string>
<string name="select_at_least_n_more_contacts">Please select at least %d more contacts</string>
<string name="select_no_more_than_n_contacts">Too many! Please select no more than %d contacts</string>
<string name="threshold">Choose the minimum number of trusted contacts needed to restore your account</string>
<string name="threshold">Set the minimum number of trusted contacts needed</string>
<string name="threshold_disabled">Two trusted contacts will be needed to restore your account</string>
<string name="threshold_secure">Secure</string>
<string name="threshold_recommended">Secure - recommended threshold</string>
<string name="threshold_low_insecure">Insecure higher threshold recommended</string>
<string name="threshold_high_insecure">Danger of loss lower threshold recommended</string>
<string name="threshold_too_few_custodians">Danger of loss - more contacts recommended</string>
<string name="threshold_defined">Choose Threshold</string>
<string name="threshold_defined">Choose Minimum Needed</string>
<string name="threshold_m_of_n">%d of %d contacts needed to recover your account</string>
<string name="backup_created">Social Backup created</string>
<string name="backup_done_info">Backup pieces sent to trusted contacts</string>
<string name="backup_created">Backup created 1/6/2020</string>
<string name="social_backup_setup_explainer_title">Backup your Briar identity and contacts list</string>
<string name="social_backup_setup_explainer_long">Choose a set of trusted contacts to send backup pieces to.\n\nYou decide how many of these contacts must help you recover your account.\n\nFor example, you can choose three trusted contacts and that any two of them are needed to recover.</string>
<string name="social_backup_setup_explainer_button">Choose trusted contacts</string>
<string name="social_backup_onboarding_title">Backup your Briar account</string>
<string name="social_backup_onboarding_long">Social backup feature allows you to recover your account with the help of trusted contacts</string>
<!-- recovery from the secret owner's POV -->
@@ -700,7 +706,7 @@
<string name="title_distributed_backup">Social Backup</string>
<string name="title_select_custodians">Select Trusted Contacts</string>
<string name="title_define_threshold">Threshold</string>
<string name="title_define_threshold">Minimum Needed</string>
<string name="title_recovery_mode">Recovery Mode</string>
<string name="title_help_recover">Help recover account</string>
@@ -713,10 +719,6 @@
<string name="setup_new_account">Create new account</string>
<string name="setup_restore_account">Restore account from backup</string>
<!-- Symbols for visualising threshold values for social backup -->
<string name="filled_bullet">\u25CF</string>
<string name="linear_bullet">\u25CB</string>
<!-- activity names -->
<string name="activity_name_distributed_backup">Social Backup</string>
<string name="activity_name_restore_account">Restore Account</string>
@@ -728,8 +730,11 @@
<string name="activity_name_new_or_recover_account">Create new account or recover existing account</string>
<string name="activity_name_recovery">Recover Account</string>
<string name="activity_name_custodian_help_recovery">Help recover account</string>
<string name="existing_backup_explain">%d of the following contacts are needed to restore your account:</string>
<string name="existing_backup_explain">Any %d of the following contacts are needed to restore your Briar account, should you lose access to it.</string>
<string name="social_backup_not_enough_contacts">To make a social backup, you need at least 2 contacts in your contacts list</string>
<string name="reading_contacts_error">There was an error reading your contacts list</string>
<!-- Display existing backup -->
<string name="social_backup_trusted_contacts">Trusted Contacts</string>
</resources>

View File

@@ -96,7 +96,7 @@
app:iconSpaceReserved="false">
<intent
android:targetClass="org.briarproject.briar.android.socialbackup.DistributedBackupActivity"
android:targetClass="org.briarproject.briar.android.socialbackup.SocialBackupSetupActivity"
android:targetPackage="@string/app_package" />
</Preference>

View File

@@ -78,4 +78,11 @@ public interface SocialBackupManager extends
*/
byte[] getReturnShardPayloadBytes(Transaction txn, ContactId contactId)
throws DbException;
/**
* Get a list of the contact ids of your custodians, or an empty
* list if no backup exists.
*/
List<ContactId> getCustodianContactIds(Transaction txn);
}

View File

@@ -612,4 +612,30 @@ class SocialBackupManagerImpl extends ConversationClientImpl
results.entrySet().iterator().next();
return new Pair<>(e.getKey(), e.getValue());
}
public List<ContactId> getCustodianContactIds(Transaction txn) {
ArrayList<ContactId> contactIds = new ArrayList<>();
try {
BackupMetadata b = getBackupMetadata(txn);
if (b == null) throw new DbException();
List<Author> custodians = b.getCustodians();
for (Author custodian : custodians) {
contactIds.add(authorToContactId(txn, custodian));
}
} catch (DbException ignored) {
// Will return an empty list
}
return contactIds;
}
private ContactId authorToContactId(Transaction txn, Author author)
throws DbException {
ArrayList<Contact> contacts =
(ArrayList<Contact>) contactManager.getContacts(txn);
for (Contact c : contacts) {
if (c.getAuthor().equals(author)) return c.getId();
}
throw new DbException();
}
}