Simplify fragment transitions for mailbox pairing UI

Now, trying again always starts before scanning, so the user needs to scan the code again.
This commit is contained in:
Torsten Grote
2022-02-22 14:43:21 -03:00
parent 4390c810d1
commit 952ac2c922
11 changed files with 76 additions and 113 deletions

View File

@@ -1,59 +1,25 @@
package org.briarproject.bramble.api.mailbox; package org.briarproject.bramble.api.mailbox;
import javax.annotation.Nullable;
public abstract class MailboxPairingState { public abstract class MailboxPairingState {
/**
* The QR code payload that was scanned by the user.
* This is null if the code should not be re-used anymore in this state.
*/
@Nullable
public final String qrCodePayload;
MailboxPairingState(@Nullable String qrCodePayload) {
this.qrCodePayload = qrCodePayload;
}
public static class QrCodeReceived extends MailboxPairingState { public static class QrCodeReceived extends MailboxPairingState {
public QrCodeReceived(String qrCodePayload) {
super(qrCodePayload);
}
} }
public static class Pairing extends MailboxPairingState { public static class Pairing extends MailboxPairingState {
public Pairing(String qrCodePayload) {
super(qrCodePayload);
}
} }
public static class Paired extends MailboxPairingState { public static class Paired extends MailboxPairingState {
public Paired() {
super(null);
}
} }
public static class InvalidQrCode extends MailboxPairingState { public static class InvalidQrCode extends MailboxPairingState {
public InvalidQrCode() {
super(null);
}
} }
public static class MailboxAlreadyPaired extends MailboxPairingState { public static class MailboxAlreadyPaired extends MailboxPairingState {
public MailboxAlreadyPaired() {
super(null);
}
} }
public static class ConnectionError extends MailboxPairingState { public static class ConnectionError extends MailboxPairingState {
public ConnectionError(String qrCodePayload) {
super(qrCodePayload);
}
} }
public static class UnexpectedError extends MailboxPairingState { public static class UnexpectedError extends MailboxPairingState {
public UnexpectedError(String qrCodePayload) {
super(qrCodePayload);
}
} }
} }

View File

@@ -71,7 +71,7 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
this.clock = clock; this.clock = clock;
this.api = api; this.api = api;
this.mailboxSettingsManager = mailboxSettingsManager; this.mailboxSettingsManager = mailboxSettingsManager;
state = new MailboxPairingState.QrCodeReceived(payload); state = new MailboxPairingState.QrCodeReceived();
} }
@Override @Override
@@ -100,15 +100,15 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
} catch (MailboxAlreadyPairedException e) { } catch (MailboxAlreadyPairedException e) {
onMailboxError(e, new MailboxPairingState.MailboxAlreadyPaired()); onMailboxError(e, new MailboxPairingState.MailboxAlreadyPaired());
} catch (IOException e) { } catch (IOException e) {
onMailboxError(e, new MailboxPairingState.ConnectionError(payload)); onMailboxError(e, new MailboxPairingState.ConnectionError());
} catch (ApiException | DbException e) { } catch (ApiException | DbException e) {
onMailboxError(e, new MailboxPairingState.UnexpectedError(payload)); onMailboxError(e, new MailboxPairingState.UnexpectedError());
} }
} }
private void pairMailbox() throws IOException, ApiException, DbException { private void pairMailbox() throws IOException, ApiException, DbException {
MailboxProperties mailboxProperties = decodeQrCodePayload(payload); MailboxProperties mailboxProperties = decodeQrCodePayload(payload);
setState(new MailboxPairingState.Pairing(payload)); setState(new MailboxPairingState.Pairing());
MailboxAuthToken ownerToken = api.setup(mailboxProperties); MailboxAuthToken ownerToken = api.setup(mailboxProperties);
MailboxProperties ownerProperties = new MailboxProperties( MailboxProperties ownerProperties = new MailboxProperties(
mailboxProperties.getBaseUrl(), ownerToken, true); mailboxProperties.getBaseUrl(), ownerToken, true);

View File

@@ -49,6 +49,7 @@ import org.briarproject.briar.android.mailbox.MailboxScanFragment;
import org.briarproject.briar.android.mailbox.MailboxStatusFragment; import org.briarproject.briar.android.mailbox.MailboxStatusFragment;
import org.briarproject.briar.android.mailbox.OfflineFragment; import org.briarproject.briar.android.mailbox.OfflineFragment;
import org.briarproject.briar.android.mailbox.SetupDownloadFragment; import org.briarproject.briar.android.mailbox.SetupDownloadFragment;
import org.briarproject.briar.android.mailbox.SetupIntroFragment;
import org.briarproject.briar.android.removabledrive.ChooserFragment; import org.briarproject.briar.android.removabledrive.ChooserFragment;
import org.briarproject.briar.android.removabledrive.ReceiveFragment; import org.briarproject.briar.android.removabledrive.ReceiveFragment;
import org.briarproject.briar.android.removabledrive.SendFragment; import org.briarproject.briar.android.removabledrive.SendFragment;
@@ -245,6 +246,8 @@ public interface AndroidComponent
void inject(BluetoothIntroFragment bluetoothIntroFragment); void inject(BluetoothIntroFragment bluetoothIntroFragment);
void inject(SetupIntroFragment setupIntroFragment);
void inject(SetupDownloadFragment setupDownloadFragment); void inject(SetupDownloadFragment setupDownloadFragment);
void inject(MailboxScanFragment mailboxScanFragment); void inject(MailboxScanFragment mailboxScanFragment);

View File

@@ -59,10 +59,7 @@ public class ErrorFragment extends FinalFragment {
// Do not hijack back button events, but let the activity process them // Do not hijack back button events, but let the activity process them
onBackPressedCallback.remove(); onBackPressedCallback.remove();
buttonView.setText(R.string.try_again_button); buttonView.setText(R.string.try_again_button);
buttonView.setOnClickListener(view -> { buttonView.setOnClickListener(view -> viewModel.showDownloadFragment());
getParentFragmentManager().popBackStackImmediate();
viewModel.tryAgainAfterError();
});
return v; return v;
} }

View File

@@ -54,6 +54,8 @@ public class MailboxActivity extends BriarActivity {
viewModel.getState().observeEvent(this, state -> { viewModel.getState().observeEvent(this, state -> {
if (state instanceof MailboxState.NotSetup) { if (state instanceof MailboxState.NotSetup) {
onNotSetup(); onNotSetup();
} else if (state instanceof MailboxState.ShowDownload) {
onShowDownload();
} else if (state instanceof MailboxState.ScanningQrCode) { } else if (state instanceof MailboxState.ScanningQrCode) {
onScanningQrCode(); onScanningQrCode();
} else if (state instanceof MailboxState.Pairing) { } else if (state instanceof MailboxState.Pairing) {
@@ -62,6 +64,8 @@ public class MailboxActivity extends BriarActivity {
onMailboxPairingStateChanged(s); onMailboxPairingStateChanged(s);
} else if (state instanceof MailboxState.OfflineWhenPairing) { } else if (state instanceof MailboxState.OfflineWhenPairing) {
onOffline(); onOffline();
} else if (state instanceof MailboxState.CameraError) {
onCameraError();
} else if (state instanceof MailboxState.IsPaired) { } else if (state instanceof MailboxState.IsPaired) {
onIsPaired(); onIsPaired();
} else { } else {
@@ -99,32 +103,38 @@ public class MailboxActivity extends BriarActivity {
.commit(); .commit();
} }
private void onScanningQrCode() { private void onShowDownload() {
FragmentManager fm = getSupportFragmentManager(); FragmentManager fm = getSupportFragmentManager();
if (fm.findFragmentByTag(MailboxScanFragment.TAG) != null) { if (fm.findFragmentByTag(SetupDownloadFragment.TAG) != null) {
// if the scanner is already on the back stack, pop back to it // if the fragment is already on the back stack, pop back to it
// instead of adding it to the stack again // instead of adding it to the stack again
fm.popBackStackImmediate(MailboxScanFragment.TAG, 0); fm.popBackStackImmediate(SetupDownloadFragment.TAG, 0);
} else { } else {
showFragment(fm, new MailboxScanFragment(), showFragment(fm, new SetupDownloadFragment(),
MailboxScanFragment.TAG); SetupDownloadFragment.TAG);
} }
} }
private void onScanningQrCode() {
showFragment(getSupportFragmentManager(), new MailboxScanFragment(),
MailboxScanFragment.TAG);
}
private void onMailboxPairingStateChanged(MailboxPairingState s) { private void onMailboxPairingStateChanged(MailboxPairingState s) {
progressBar.setVisibility(INVISIBLE); progressBar.setVisibility(INVISIBLE);
FragmentManager fm = getSupportFragmentManager(); FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() == 0) {
// We re-launched into an existing state,
// need to re-populate the back stack.
onNotSetup();
onShowDownload();
}
Fragment f; Fragment f;
String tag; String tag;
if (s instanceof MailboxPairingState.QrCodeReceived) { if (s instanceof MailboxPairingState.QrCodeReceived) {
// ignore, showing yet another progress fragment messes with back stack f = new MailboxConnectingFragment();
return; tag = MailboxConnectingFragment.TAG;
} else if (s instanceof MailboxPairingState.Pairing) { } else if (s instanceof MailboxPairingState.Pairing) {
if (fm.getBackStackEntryCount() == 0) {
// We re-launched into an existing state,
// need to re-populate the back stack.
repopulateBackStack();
}
f = new MailboxConnectingFragment(); f = new MailboxConnectingFragment();
tag = MailboxConnectingFragment.TAG; tag = MailboxConnectingFragment.TAG;
} else if (s instanceof MailboxPairingState.InvalidQrCode) { } else if (s instanceof MailboxPairingState.InvalidQrCode) {
@@ -164,18 +174,17 @@ public class MailboxActivity extends BriarActivity {
OfflineFragment.TAG); OfflineFragment.TAG);
} }
private void onCameraError() {
Fragment f = ErrorFragment.newInstance(
R.string.mailbox_setup_camera_error_title,
R.string.mailbox_setup_camera_error_description);
showFragment(getSupportFragmentManager(), f, ErrorFragment.TAG);
}
private void onIsPaired() { private void onIsPaired() {
progressBar.setVisibility(INVISIBLE); progressBar.setVisibility(INVISIBLE);
showFragment(getSupportFragmentManager(), new MailboxStatusFragment(), showFragment(getSupportFragmentManager(), new MailboxStatusFragment(),
MailboxStatusFragment.TAG, false); MailboxStatusFragment.TAG, false);
} }
private void repopulateBackStack() {
FragmentManager fm = getSupportFragmentManager();
onNotSetup();
showFragment(fm, new SetupDownloadFragment(),
SetupDownloadFragment.TAG);
onScanningQrCode();
}
} }

View File

@@ -94,7 +94,7 @@ public class MailboxScanFragment extends Fragment {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
Toast.makeText(requireContext(), R.string.camera_error, Toast.makeText(requireContext(), R.string.camera_error,
LENGTH_LONG).show(); LENGTH_LONG).show();
requireActivity().getSupportFragmentManager().popBackStack(); viewModel.onCameraError();
} }
} }

View File

@@ -3,13 +3,14 @@ package org.briarproject.briar.android.mailbox;
import org.briarproject.bramble.api.mailbox.MailboxPairingState; import org.briarproject.bramble.api.mailbox.MailboxPairingState;
import org.briarproject.bramble.api.mailbox.MailboxStatus; import org.briarproject.bramble.api.mailbox.MailboxStatus;
import androidx.annotation.Nullable;
class MailboxState { class MailboxState {
static class NotSetup extends MailboxState { static class NotSetup extends MailboxState {
} }
static class ShowDownload extends MailboxState {
}
static class ScanningQrCode extends MailboxState { static class ScanningQrCode extends MailboxState {
} }
@@ -19,24 +20,12 @@ class MailboxState {
Pairing(MailboxPairingState pairingState) { Pairing(MailboxPairingState pairingState) {
this.pairingState = pairingState; this.pairingState = pairingState;
} }
@Nullable
String getQrCodePayload() {
return pairingState.qrCodePayload;
}
} }
static class OfflineWhenPairing extends MailboxState { static class OfflineWhenPairing extends MailboxState {
@Nullable }
final String qrCodePayload;
OfflineWhenPairing(@Nullable String qrCodePayload) { static class CameraError extends MailboxState {
this.qrCodePayload = qrCodePayload;
}
OfflineWhenPairing() {
this(null);
}
} }
static class IsPaired extends MailboxState { static class IsPaired extends MailboxState {

View File

@@ -33,7 +33,6 @@ import androidx.annotation.AnyThread;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import static java.util.Objects.requireNonNull;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
@@ -110,6 +109,11 @@ class MailboxViewModel extends DbViewModel
} }
} }
@UiThread
void onCameraError() {
state.setEvent(new MailboxState.CameraError());
}
@Override @Override
@IoExecutor @IoExecutor
public void onQrCodeDecoded(Result result) { public void onQrCodeDecoded(Result result) {
@@ -123,7 +127,7 @@ class MailboxViewModel extends DbViewModel
pairingTask = mailboxManager.startPairingTask(qrCodePayload); pairingTask = mailboxManager.startPairingTask(qrCodePayload);
pairingTask.addObserver(this); pairingTask.addObserver(this);
} else { } else {
state.postEvent(new MailboxState.OfflineWhenPairing(qrCodePayload)); state.postEvent(new MailboxState.OfflineWhenPairing());
} }
} }
@@ -143,26 +147,8 @@ class MailboxViewModel extends DbViewModel
} }
@UiThread @UiThread
void tryAgainWhenOffline() { void showDownloadFragment() {
MailboxState.OfflineWhenPairing offline = state.setEvent(new MailboxState.ShowDownload());
(MailboxState.OfflineWhenPairing) requireNonNull(
state.getLastValue());
if (offline.qrCodePayload == null) {
onScanButtonClicked();
} else {
onQrCodePayloadReceived(offline.qrCodePayload);
}
}
@UiThread
void tryAgainAfterError() {
MailboxState.Pairing pairing = (MailboxState.Pairing)
requireNonNull(state.getLastValue());
if (pairing.getQrCodePayload() == null) {
onScanButtonClicked();
} else {
onQrCodePayloadReceived(pairing.getQrCodePayload());
}
} }
@UiThread @UiThread

View File

@@ -62,10 +62,7 @@ public class OfflineFragment extends Fragment {
startActivity(i); startActivity(i);
}); });
buttonView = v.findViewById(R.id.button); buttonView = v.findViewById(R.id.button);
buttonView.setOnClickListener(view -> { buttonView.setOnClickListener(view -> viewModel.showDownloadFragment());
getParentFragmentManager().popBackStackImmediate();
viewModel.tryAgainWhenOffline();
});
return v; return v;
} }

View File

@@ -1,5 +1,6 @@
package org.briarproject.briar.android.mailbox; package org.briarproject.briar.android.mailbox;
import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@@ -11,12 +12,15 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import javax.inject.Inject;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.ViewModelProvider;
import static android.view.View.FOCUS_DOWN; import static android.view.View.FOCUS_DOWN;
import static org.briarproject.briar.android.util.UiUtils.showFragment; import static org.briarproject.briar.android.AppModule.getAndroidComponent;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -24,8 +28,22 @@ public class SetupIntroFragment extends Fragment {
static final String TAG = SetupIntroFragment.class.getName(); static final String TAG = SetupIntroFragment.class.getName();
@Inject
ViewModelProvider.Factory viewModelFactory;
private MailboxViewModel viewModel;
private ScrollView scrollView; private ScrollView scrollView;
@Override
public void onAttach(Context context) {
super.onAttach(context);
FragmentActivity activity = requireActivity();
getAndroidComponent(activity).inject(this);
viewModel = new ViewModelProvider(activity, viewModelFactory)
.get(MailboxViewModel.class);
}
@Nullable @Nullable
@Override @Override
public View onCreateView(LayoutInflater inflater, public View onCreateView(LayoutInflater inflater,
@@ -35,11 +53,7 @@ public class SetupIntroFragment extends Fragment {
container, false); container, false);
scrollView = v.findViewById(R.id.scrollView); scrollView = v.findViewById(R.id.scrollView);
Button button = v.findViewById(R.id.continueButton); Button button = v.findViewById(R.id.continueButton);
button.setOnClickListener(view -> { button.setOnClickListener(view -> viewModel.showDownloadFragment());
FragmentManager fm = getParentFragmentManager();
Fragment f = new SetupDownloadFragment();
showFragment(fm, f, SetupDownloadFragment.TAG);
});
return v; return v;
} }

View File

@@ -631,6 +631,8 @@
<string name="mailbox_setup_io_error_description">Ensure that both devices are connected to the internet and try again.</string> <string name="mailbox_setup_io_error_description">Ensure that both devices are connected to the internet and try again.</string>
<string name="mailbox_setup_assertion_error_title">Mailbox error</string> <string name="mailbox_setup_assertion_error_title">Mailbox error</string>
<string name="mailbox_setup_assertion_error_description">Please send feedback (with anonymous data) via the Briar app if the issue persists.</string> <string name="mailbox_setup_assertion_error_description">Please send feedback (with anonymous data) via the Briar app if the issue persists.</string>
<string name="mailbox_setup_camera_error_title" translatable="false">@string/camera_error</string>
<string name="mailbox_setup_camera_error_description">Could not access camera. Try again, maybe after rebooting device.</string>
<string name="mailbox_setup_paired_title">Connected</string> <string name="mailbox_setup_paired_title">Connected</string>
<string name="mailbox_setup_paired_description">Your Mailbox has been successfully linked with Briar.\n <string name="mailbox_setup_paired_description">Your Mailbox has been successfully linked with Briar.\n
\nKeep your Mailbox connected to power and Wi-Fi so it\'s always online.</string> \nKeep your Mailbox connected to power and Wi-Fi so it\'s always online.</string>