UI for error and explainer fragments

This commit is contained in:
ameba23
2021-04-26 12:29:45 +02:00
parent 8ffe9a6367
commit d78a6604fd
12 changed files with 170 additions and 39 deletions

View File

@@ -88,6 +88,7 @@ import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardA
import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardErrorFragment; import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardErrorFragment;
import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardFragment; import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardFragment;
import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardSuccessFragment; 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.OwnerRecoveryModeExplainerFragment;
import org.briarproject.briar.android.socialbackup.recover.OwnerRecoveryModeMainFragment; import org.briarproject.briar.android.socialbackup.recover.OwnerRecoveryModeMainFragment;
import org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardActivity; import org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardActivity;
@@ -302,5 +303,7 @@ public interface ActivityComponent {
void inject(OwnerReturnShardSuccessFragment ownerReturnShardSuccessFragment); void inject(OwnerReturnShardSuccessFragment ownerReturnShardSuccessFragment);
void inject(OwnerRecoveryModeErrorFragment ownerRecoveryModeErrorFragment);
void inject(CustodianReturnShardErrorFragment custodianReturnShardErrorFragment); void inject(CustodianReturnShardErrorFragment custodianReturnShardErrorFragment);
} }

View File

@@ -21,6 +21,7 @@ import javax.inject.Inject;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.briar.android.conversation.ConversationActivity.CONTACT_ID; import static org.briarproject.briar.android.conversation.ConversationActivity.CONTACT_ID;
@@ -30,6 +31,7 @@ public class CustodianReturnShardActivity extends BriarActivity
private CustodianReturnShardViewModel viewModel; private CustodianReturnShardViewModel viewModel;
private static final Logger LOG = private static final Logger LOG =
getLogger(CustodianReturnShardActivity.class.getName()); getLogger(CustodianReturnShardActivity.class.getName());
private ContactId contactId;
@Inject @Inject
ViewModelProvider.Factory viewModelFactory; ViewModelProvider.Factory viewModelFactory;
@@ -50,35 +52,61 @@ public class CustodianReturnShardActivity extends BriarActivity
Intent intent = getIntent(); Intent intent = getIntent();
int id = intent.getIntExtra(CONTACT_ID, -1); int id = intent.getIntExtra(CONTACT_ID, -1);
if (id == -1) throw new IllegalStateException("No ContactId"); if (id == -1) throw new IllegalStateException("No ContactId");
ContactId contactId = new ContactId(id); contactId = new ContactId(id);
try { try {
viewModel.start(contactId); viewModel.start(contactId);
} catch (IOException e) { // } catch (IOException e) {
// TODO improve this // // TODO improve this
Toast.makeText(this, // Toast.makeText(this,
"It looks like you are not connected to a Wifi network", // "It looks like you are not connected to a Wifi network",
Toast.LENGTH_SHORT).show(); // Toast.LENGTH_SHORT).show();
showNextFragment(new CustodianReturnShardErrorFragment()); // showInitialFragment(new CustodianReturnShardErrorFragment());
return;
} catch (DbException e) { } catch (DbException e) {
Toast.makeText(this, Toast.makeText(this,
"You do not hold a backup piece for this contact", "You do not hold a backup piece for this contact",
Toast.LENGTH_SHORT).show(); Toast.LENGTH_SHORT).show();
finish(); finish();
} }
showInitialFragment(new CustodianRecoveryModeExplainerFragment());
} }
showInitialFragment(new CustodianRecoveryModeExplainerFragment());
viewModel.getContinueClicked().observeEvent(this, clicked -> {
if (clicked) beginTransfer();
});
viewModel.getShowCameraFragment().observeEvent(this, show -> { viewModel.getShowCameraFragment().observeEvent(this, show -> {
if (show) showCameraFragment(); if (show) showCameraFragment();
}); });
viewModel.getSuccessDismissed().observeEvent(this, dismissed -> { viewModel.getSuccessDismissed().observeEvent(this, dismissed -> {
if (dismissed) finish(); if (dismissed) finish();
}); });
viewModel.getErrorTryAgain().observeEvent(this, tryAgain -> {
if (tryAgain) beginTransfer();
});
viewModel.getState() viewModel.getState()
.observe(this, this::onReturnShardStateChanged); .observe(this, this::onReturnShardStateChanged);
} }
private void beginTransfer() {
try {
viewModel.beginTransfer();
} catch (IOException e) {
Toast.makeText(this,
"It looks like you are not connected to a Wifi network",
Toast.LENGTH_SHORT).show();
FragmentManager fm = getSupportFragmentManager();
if (fm.findFragmentByTag(CustodianReturnShardFragment.TAG) == null) {
BaseFragment f = new CustodianReturnShardErrorFragment();
fm.beginTransaction()
.replace(R.id.fragmentContainer, f, f.getUniqueTag())
.addToBackStack(f.getUniqueTag())
.commit();
}
}
}
private void onReturnShardStateChanged(CustodianTask.State state) { private void onReturnShardStateChanged(CustodianTask.State state) {
if (state instanceof CustodianTask.State.Success) { if (state instanceof CustodianTask.State.Success) {
CustodianReturnShardSuccessFragment fragment = new CustodianReturnShardSuccessFragment(); CustodianReturnShardSuccessFragment fragment = new CustodianReturnShardSuccessFragment();

View File

@@ -41,13 +41,12 @@ public class CustodianReturnShardErrorFragment extends BaseFragment {
public View onCreateView(@NonNull LayoutInflater inflater, public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) { @Nullable Bundle savedInstanceState) {
// TODO change fragment to error View view = inflater.inflate(R.layout.fragment_recovery_custodian_error_explainer,
View view = inflater.inflate(R.layout.fragment_recovery_custodian_done,
container, false); container, false);
Button button = view.findViewById(R.id.button); Button button = view.findViewById(R.id.button);
button.setOnClickListener(e -> viewModel.onErrorCancelled()); button.setOnClickListener(e -> viewModel.onErrorTryAgain());
// TODO try again button // TODO cancel button
// button.setOnClickListener(e -> viewModel.onErrorCancelled());
return view; return view;
} }

View File

@@ -51,9 +51,9 @@ public class CustodianReturnShardViewModel extends AndroidViewModel
private final SocialBackupManager socialBackupManager; private final SocialBackupManager socialBackupManager;
private final DatabaseComponent db; private final DatabaseComponent db;
final QrCodeDecoder qrCodeDecoder; final QrCodeDecoder qrCodeDecoder;
private boolean wasContinueClicked = false;
private boolean qrCodeRead = false; private boolean qrCodeRead = false;
private WifiManager wifiManager; private WifiManager wifiManager;
private final MutableLiveEvent<Boolean > continueClicked = new MutableLiveEvent<>();
private final MutableLiveEvent<Boolean> showCameraFragment = private final MutableLiveEvent<Boolean> showCameraFragment =
new MutableLiveEvent<>(); new MutableLiveEvent<>();
private final MutableLiveEvent<Boolean> successDismissed = private final MutableLiveEvent<Boolean> successDismissed =
@@ -111,12 +111,8 @@ public class CustodianReturnShardViewModel extends AndroidViewModel
} }
} }
public void start(ContactId contactId) throws DbException, IOException { public void start(ContactId contactId) throws DbException {
InetAddress inetAddress = getWifiIpv4Address(); // TODO this should be transactionWithResult
LOG.info("Client InetAddress: " + inetAddress);
if (inetAddress == null)
throw new IOException("Cannot get IP on local wifi");
db.transaction(false, txn -> { db.transaction(false, txn -> {
if (!socialBackupManager.amCustodian(txn, contactId)) { if (!socialBackupManager.amCustodian(txn, contactId)) {
throw new DbException(); throw new DbException();
@@ -124,8 +120,6 @@ public class CustodianReturnShardViewModel extends AndroidViewModel
returnShardPayloadBytes = socialBackupManager returnShardPayloadBytes = socialBackupManager
.getReturnShardPayloadBytes(txn, contactId); .getReturnShardPayloadBytes(txn, contactId);
}); });
task.cancel();
task.start(this, returnShardPayloadBytes);
} }
@IoExecutor @IoExecutor
@@ -150,11 +144,21 @@ public class CustodianReturnShardViewModel extends AndroidViewModel
} }
} }
public void beginTransfer() throws IOException {
InetAddress inetAddress = getWifiIpv4Address();
LOG.info("Client InetAddress: " + inetAddress);
if (inetAddress == null)
throw new IOException("Cannot get IP on local wifi");
task.cancel();
task.start(this, returnShardPayloadBytes);
//TODO camera permissions
showCameraFragment.setEvent(true);
}
@UiThread @UiThread
public void onContinueClicked() { public void onContinueClicked() {
wasContinueClicked = true; continueClicked.setEvent(true);
// checkPermissions.setEvent(true); // checkPermissions.setEvent(true);
showCameraFragment.setEvent(true);
} }
@UiThread @UiThread
@@ -201,4 +205,6 @@ public class CustodianReturnShardViewModel extends AndroidViewModel
public MutableLiveEvent<Boolean> getErrorTryAgain() { public MutableLiveEvent<Boolean> getErrorTryAgain() {
return errorTryAgain; return errorTryAgain;
} }
public MutableLiveEvent<Boolean> getContinueClicked() { return continueClicked; }
} }

View File

@@ -1,4 +1,57 @@
package org.briarproject.briar.android.socialbackup.recover; package org.briarproject.briar.android.socialbackup.recover;
public class OwnerRecoveryModeErrorFragment { import android.os.Bundle;
import android.view.LayoutInflater;
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;
import javax.inject.Inject;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class OwnerRecoveryModeErrorFragment extends BaseFragment {
public static final String TAG =
OwnerRecoveryModeErrorFragment.class.getName();
@Inject
ViewModelProvider.Factory viewModelFactory;
private OwnerReturnShardViewModel viewModel;
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
viewModel = new ViewModelProvider(requireActivity(), viewModelFactory)
.get(OwnerReturnShardViewModel.class);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_recovery_custodian_error_explainer,
container, false);
Button button = view.findViewById(R.id.button);
button.setOnClickListener(e -> viewModel.onErrorTryAgain());
// TODO cancel button
// button.setOnClickListener(e -> viewModel.onErrorCancelled());
return view;
}
@Override
public String getUniqueTag() {
return TAG;
}
} }

View File

@@ -80,6 +80,9 @@ public class OwnerReturnShardActivity extends BaseActivity
viewModel.getSuccessDismissed().observeEvent(this, success -> { viewModel.getSuccessDismissed().observeEvent(this, success -> {
if (success) onSuccessDismissed(); if (success) onSuccessDismissed();
}); });
viewModel.getErrorTryAgain().observeEvent(this, tryAgain -> {
if (tryAgain) onBackPressed();
});
viewModel.getState() viewModel.getState()
.observe(this, this::onReturnShardStateChanged); .observe(this, this::onReturnShardStateChanged);
} }
@@ -167,12 +170,11 @@ public class OwnerReturnShardActivity extends BaseActivity
} }
onBackPressed(); onBackPressed();
} else if (state instanceof SecretOwnerTask.State.Failure) { } else if (state instanceof SecretOwnerTask.State.Failure) {
// TODO error screen, handle reason // Toast.makeText(this,
Toast.makeText(this, // "Shard return failed!",
"Shard return failed!", // Toast.LENGTH_SHORT).show();
Toast.LENGTH_SHORT).show(); // onBackPressed();
onBackPressed(); showNextFragment(new OwnerRecoveryModeErrorFragment());
// showNextFragment(new OwnerRecoveryModeExplainerFragment());
} }
} }

View File

@@ -38,7 +38,7 @@ public class OwnerReturnShardSuccessFragment extends BaseFragment {
public View onCreateView(@NonNull LayoutInflater inflater, public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) { @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_recovery_custodian_done, View view = inflater.inflate(R.layout.fragment_recovery_owner_success,
container, false); container, false);
Button button = view.findViewById(R.id.button); Button button = view.findViewById(R.id.button);

View File

@@ -51,6 +51,8 @@ class OwnerReturnShardViewModel extends AndroidViewModel
private final SecretOwnerTask task; private final SecretOwnerTask task;
private final RestoreAccount restoreAccount; private final RestoreAccount restoreAccount;
private final MutableLiveEvent<Boolean> errorTryAgain =
new MutableLiveEvent<>();
private final MutableLiveEvent<Boolean> showQrCodeFragment = private final MutableLiveEvent<Boolean> showQrCodeFragment =
new MutableLiveEvent<>(); new MutableLiveEvent<>();
private final MutableLiveEvent<Boolean> successDismissed = new MutableLiveEvent<>(); private final MutableLiveEvent<Boolean> successDismissed = new MutableLiveEvent<>();
@@ -158,6 +160,10 @@ class OwnerReturnShardViewModel extends AndroidViewModel
}); });
} }
@UiThread
public void onErrorTryAgain() {
errorTryAgain.setEvent(true);
}
/** /**
* Set to true in onPostResume() and false in onPause(). This prevents the * Set to true in onPostResume() and false in onPause(). This prevents the
@@ -236,4 +242,8 @@ class OwnerReturnShardViewModel extends AndroidViewModel
public MutableLiveEvent<Boolean> getSuccessDismissed() { public MutableLiveEvent<Boolean> getSuccessDismissed() {
return successDismissed; return successDismissed;
} }
public MutableLiveEvent<Boolean> getErrorTryAgain() {
return errorTryAgain;
}
} }

View File

@@ -31,7 +31,7 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView1" app:layout_constraintTop_toBottomOf="@+id/textView1"
app:srcCompat="@drawable/qr_code_error" /> app:srcCompat="@drawable/qr_code_social_backup_error" />
<TextView <TextView
android:id="@+id/textView2" android:id="@+id/textView2"

View File

@@ -31,7 +31,20 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" app:layout_constraintTop_toBottomOf="@+id/textView"
app:srcCompat="@drawable/qr_code_intro" /> app:srcCompat="@drawable/qr_code_social_backup" />
<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/imageView"
tools:layout_editor_absoluteX="16dp" />
<Button <Button
android:id="@+id/button" android:id="@+id/button"
@@ -42,6 +55,6 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView" /> app:layout_constraintTop_toBottomOf="@+id/textViewExplain" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -31,7 +31,21 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" app:layout_constraintTop_toBottomOf="@+id/textView"
app:srcCompat="@drawable/qr_code_intro" /> app:srcCompat="@drawable/qr_code_social_backup" />
<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/recovery_explainer_extra"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView"
tools:layout_editor_absoluteX="16dp" />
<Button <Button
android:id="@+id/beginButton" android:id="@+id/beginButton"
@@ -42,6 +56,6 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView" /> app:layout_constraintTop_toBottomOf="@+id/textViewExplain" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -676,19 +676,22 @@
<!-- recovery from the secret owner's POV --> <!-- recovery from the secret owner's POV -->
<string name="recovery_explainer">You need to meet your trusted contacts in-person to receive pieces</string> <string name="recovery_explainer">You need to meet your trusted contacts in-person to receive pieces</string>
<string name="recovery_explainer_extra">Your trusted contact must scan a QR code to initiate the transfer.\n\nYou must both be connected to the same wifi network</string>
<string name="recovery_begin">Begin</string> <string name="recovery_begin">Begin</string>
<string name="recovery_failed_to_receive">Failed to receive backup piece</string> <string name="recovery_failed_to_receive">Failed to receive backup piece</string>
<string name="recovery_helpful_suggestions">Please check that bluetooth is swtiched on and that no-one but your trusted contact is able to scan the QR code</string> <string name="recovery_helpful_suggestions">Please check that you are both connected to the same wifi network and try again</string>
<string name="recovery_retry">Retry</string> <string name="recovery_retry">Retry</string>
<string name="recovery_recovered_shards">Recovered backup pieces:</string> <string name="recovery_recovered_shards">Recovered backup pieces:</string>
<string name="recovery_scan_qr_code">Show QR code</string> <string name="recovery_scan_qr_code">Show QR code</string>
<string name="recovery_recovering_account">Recovering account…</string> <string name="recovery_recovering_account">Recovering account…</string>
<string name="recovery_shard_received">Account backup piece received</string> <string name="recovery_shard_received">Account backup piece received</string>
<string name="recovery_account_recovered">Account recovered</string> <string name="recovery_account_recovered">Account recovered</string>
<string name="recovery_account_recovered_explain">You must now set a new password for your account</string>
<!-- recovery from the custodian's POV --> <!-- recovery from the custodian's POV -->
<string name="custodian_recovery_explainer">You need to meet in-person to transfer backup piece</string> <string name="custodian_recovery_explainer">You need to meet in-person to transfer backup piece</string>
<string name="custodian_recovery_explainer_extra">Make sure you are both connected to the same wifi network, and scan the QR code on your contact\'s device to begin transferring the backup piece</string>
<string name="custodian_recovery_failed_to_send">Failed to send backup piece</string> <string name="custodian_recovery_failed_to_send">Failed to send backup piece</string>
<string name="custodian_scan_code">Scan code</string> <string name="custodian_scan_code">Scan code</string>
<string name="custodian_shard_sent">Account backup piece transmitted</string> <string name="custodian_shard_sent">Account backup piece transmitted</string>