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.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;
@@ -302,5 +303,7 @@ public interface ActivityComponent {
void inject(OwnerReturnShardSuccessFragment ownerReturnShardSuccessFragment);
void inject(OwnerRecoveryModeErrorFragment ownerRecoveryModeErrorFragment);
void inject(CustodianReturnShardErrorFragment custodianReturnShardErrorFragment);
}

View File

@@ -21,6 +21,7 @@ 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;
@@ -30,6 +31,7 @@ public class CustodianReturnShardActivity extends BriarActivity
private CustodianReturnShardViewModel viewModel;
private static final Logger LOG =
getLogger(CustodianReturnShardActivity.class.getName());
private ContactId contactId;
@Inject
ViewModelProvider.Factory viewModelFactory;
@@ -50,35 +52,61 @@ public class CustodianReturnShardActivity extends BriarActivity
Intent intent = getIntent();
int id = intent.getIntExtra(CONTACT_ID, -1);
if (id == -1) throw new IllegalStateException("No ContactId");
ContactId contactId = new ContactId(id);
contactId = new ContactId(id);
try {
viewModel.start(contactId);
} catch (IOException e) {
// TODO improve this
Toast.makeText(this,
"It looks like you are not connected to a Wifi network",
Toast.LENGTH_SHORT).show();
showNextFragment(new CustodianReturnShardErrorFragment());
return;
// } catch (IOException e) {
// // TODO improve this
// Toast.makeText(this,
// "It looks like you are not connected to a Wifi network",
// Toast.LENGTH_SHORT).show();
// showInitialFragment(new CustodianReturnShardErrorFragment());
} catch (DbException e) {
Toast.makeText(this,
"You do not hold a backup piece for this contact",
Toast.LENGTH_SHORT).show();
finish();
}
showInitialFragment(new CustodianRecoveryModeExplainerFragment());
}
showInitialFragment(new CustodianRecoveryModeExplainerFragment());
viewModel.getContinueClicked().observeEvent(this, clicked -> {
if (clicked) beginTransfer();
});
viewModel.getShowCameraFragment().observeEvent(this, show -> {
if (show) showCameraFragment();
});
viewModel.getSuccessDismissed().observeEvent(this, dismissed -> {
if (dismissed) finish();
});
viewModel.getErrorTryAgain().observeEvent(this, tryAgain -> {
if (tryAgain) beginTransfer();
});
viewModel.getState()
.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) {
if (state instanceof CustodianTask.State.Success) {
CustodianReturnShardSuccessFragment fragment = new CustodianReturnShardSuccessFragment();

View File

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

View File

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

View File

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

View File

@@ -38,7 +38,7 @@ public class OwnerReturnShardSuccessFragment extends BaseFragment {
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@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);
Button button = view.findViewById(R.id.button);

View File

@@ -51,6 +51,8 @@ class OwnerReturnShardViewModel extends AndroidViewModel
private final SecretOwnerTask task;
private final RestoreAccount restoreAccount;
private final MutableLiveEvent<Boolean> errorTryAgain =
new MutableLiveEvent<>();
private final MutableLiveEvent<Boolean> showQrCodeFragment =
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
@@ -236,4 +242,8 @@ class OwnerReturnShardViewModel extends AndroidViewModel
public MutableLiveEvent<Boolean> getSuccessDismissed() {
return successDismissed;
}
public MutableLiveEvent<Boolean> getErrorTryAgain() {
return errorTryAgain;
}
}

View File

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

View File

@@ -31,7 +31,20 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
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
android:id="@+id/button"
@@ -42,6 +55,6 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView" />
app:layout_constraintTop_toBottomOf="@+id/textViewExplain" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -31,7 +31,21 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
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
android:id="@+id/beginButton"
@@ -42,6 +56,6 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView" />
app:layout_constraintTop_toBottomOf="@+id/textViewExplain" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -676,19 +676,22 @@
<!-- 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_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_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_recovered_shards">Recovered backup pieces:</string>
<string name="recovery_scan_qr_code">Show QR code</string>
<string name="recovery_recovering_account">Recovering account…</string>
<string name="recovery_shard_received">Account backup piece received</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 -->
<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_scan_code">Scan code</string>
<string name="custodian_shard_sent">Account backup piece transmitted</string>