Activity and view model for custodian returning shard

This commit is contained in:
ameba23
2021-04-12 10:50:47 +02:00
parent e856ee48f9
commit 9e4ace4ce7
8 changed files with 412 additions and 88 deletions

View File

@@ -80,11 +80,13 @@ 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.CustodianDisplayFragment;
import org.briarproject.briar.android.socialbackup.CustodianHelpRecoverActivity;
import org.briarproject.briar.android.socialbackup.CustodianRecoveryModeExplainerFragment;
import org.briarproject.briar.android.socialbackup.CustodianSelectorFragment;
import org.briarproject.briar.android.socialbackup.DistributedBackupActivity;
import org.briarproject.briar.android.socialbackup.ExistingBackupFragment;
import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardActivity;
import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardFragment;
import org.briarproject.briar.android.socialbackup.recover.OwnerRecoveryModeExplainerFragment;
import org.briarproject.briar.android.socialbackup.recover.RecoverActivity;
import org.briarproject.briar.android.socialbackup.ShardsSentFragment;
@@ -202,6 +204,8 @@ public interface ActivityComponent {
void inject(ReturnShardActivity returnShardActivity);
void inject(CustodianReturnShardActivity custodianSendShardActivity);
// Fragments
void inject(AuthorNameFragment fragment);
@@ -279,4 +283,8 @@ public interface ActivityComponent {
void inject(NewOrRecoverFragment newOrRecoverFragment);
void inject(ReturnShardFragment returnShardFragment);
void inject(CustodianRecoveryModeExplainerFragment custodianRecoveryModeExplainerFragment);
void inject(CustodianReturnShardFragment custodianReturnShardFragment);
}

View File

@@ -1,48 +1,57 @@
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 org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardViewModel;
import javax.inject.Inject;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.briarproject.briar.R;
import androidx.lifecycle.ViewModelProvider;
public class CustodianRecoveryModeExplainerFragment extends BaseFragment {
protected CustodianScanQrButtonListener listener;
@Inject
ViewModelProvider.Factory viewModelFactory;
public static final String TAG = CustodianRecoveryModeExplainerFragment.class.getName();
private CustodianReturnShardViewModel viewModel;
public static final String TAG =
CustodianRecoveryModeExplainerFragment.class.getName();
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requireActivity().setTitle(R.string.title_help_recover);
public void injectFragment(ActivityComponent component) {
component.inject(this);
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
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable
ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_recovery_custodian_explainer,
container, false);
View view =
inflater.inflate(R.layout.fragment_recovery_custodian_explainer,
container, false);
Button button = view.findViewById(R.id.button);
button.setOnClickListener(e -> listener.scanQrButtonClicked());
button.setOnClickListener(e -> viewModel.onContinueClicked());
return view;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
listener = (CustodianScanQrButtonListener) context;
}
@Override
public String getUniqueTag() {
return TAG;

View File

@@ -0,0 +1,79 @@
package org.briarproject.briar.android.socialbackup.recover;
import android.os.Bundle;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.socialbackup.CustodianRecoveryModeExplainerFragment;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.ViewModelProvider;
import static java.util.logging.Logger.getLogger;
public class CustodianReturnShardActivity extends BriarActivity {
private CustodianReturnShardViewModel viewModel;
private static final Logger LOG =
getLogger(CustodianReturnShardActivity.class.getName());
@Inject
ViewModelProvider.Factory viewModelFactory;
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
viewModel = new ViewModelProvider(this, viewModelFactory)
.get(CustodianReturnShardViewModel.class);
}
@Override
public void onCreate(@Nullable Bundle state) {
super.onCreate(state);
// byte[] returnShardPayloadBytes =
// getIntent().getByteArrayExtra(RETURN_SHARD_PAYLOAD);
// try {
// ReturnShardPayload returnShardPayload = parseReturnShardPayload(
// clientHelper.toList(returnShardPayloadBytes));
// viewModel.setReturnShardPayload(returnShardPayload);
// } catch (FormatException e) {
// Toast.makeText(this,
// "Error reading social backup",
// Toast.LENGTH_SHORT).show();
// finish();
// }
setContentView(R.layout.activity_fragment_container);
if (state == null) {
showInitialFragment(new CustodianRecoveryModeExplainerFragment());
}
// viewModel.getCheckPermissions().observeEvent(this, check ->
// permissionManager.checkPermissions());
// viewModel.getRequestBluetoothDiscoverable().observeEvent(this, r ->
// requestBluetoothDiscoverable()); // never false
viewModel.getShowCameraFragment().observeEvent(this, show -> {
if (show) showCameraFragment();
});
// viewModel.getState()
// .observe(this, this::onReturnShardStateChanged);
}
private void showCameraFragment() {
// FIXME #824
FragmentManager fm = getSupportFragmentManager();
if (fm.findFragmentByTag(CustodianReturnShardFragment.TAG) == null) {
BaseFragment f = CustodianReturnShardFragment.newInstance();
fm.beginTransaction()
.replace(R.id.fragmentContainer, f, f.getUniqueTag())
.addToBackStack(f.getUniqueTag())
.commit();
}
}
}

View File

@@ -0,0 +1,185 @@
package org.briarproject.briar.android.socialbackup.recover;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
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.contact.add.nearby.CameraException;
import org.briarproject.briar.android.contact.add.nearby.CameraView;
import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.view.QrCodeView;
import org.briarproject.briar.api.socialbackup.recovery.CustodianTask;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import androidx.annotation.UiThread;
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;
import static android.widget.Toast.LENGTH_LONG;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class CustodianReturnShardFragment extends BaseFragment
implements QrCodeView.FullscreenListener {
public static final String TAG = CustodianReturnShardFragment.class.getName();
private static final Logger LOG = Logger.getLogger(TAG);
@Inject
ViewModelProvider.Factory viewModelFactory;
private CustodianReturnShardViewModel viewModel;
private CameraView cameraView;
private LinearLayout cameraOverlay;
private View statusView;
private TextView status;
public static CustodianReturnShardFragment newInstance() {
Bundle args = new Bundle();
CustodianReturnShardFragment fragment = new CustodianReturnShardFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
viewModel = new ViewModelProvider(requireActivity(), viewModelFactory)
.get(CustodianReturnShardViewModel.class);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_keyagreement_qr, container,
false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
cameraView = view.findViewById(R.id.camera_view);
cameraOverlay = view.findViewById(R.id.camera_overlay);
statusView = view.findViewById(R.id.status_container);
status = view.findViewById(R.id.connect_status);
viewModel.getState().observe(getViewLifecycleOwner(),
this::onReturnShardStateChanged);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
requireActivity().setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR);
cameraView.setPreviewConsumer(viewModel.getQrCodeDecoder());
}
@Override
public void onStart() {
super.onStart();
try {
cameraView.start();
} catch (CameraException e) {
logCameraExceptionAndFinish(e);
}
}
@Override
public void onStop() {
super.onStop();
try {
cameraView.stop();
} catch (CameraException e) {
logCameraExceptionAndFinish(e);
}
}
@Override
public void onDestroy() {
requireActivity()
.setRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
super.onDestroy();
}
@Override
public void setFullscreen(boolean fullscreen) {
LinearLayout.LayoutParams statusParams, qrCodeParams;
if (fullscreen) {
statusParams = new LinearLayout.LayoutParams(0, 0, 0f);
} else {
if (cameraOverlay.getOrientation() == HORIZONTAL) {
statusParams = new LinearLayout.LayoutParams(0, MATCH_PARENT, 1f);
} else {
statusParams = new LinearLayout.LayoutParams(MATCH_PARENT, 0, 1f);
}
}
statusView.setLayoutParams(statusParams);
cameraOverlay.invalidate();
}
@UiThread
private void onReturnShardStateChanged(@Nullable CustodianTask.State state) {
if (state instanceof CustodianTask.State.Connecting) {
try {
cameraView.stop();
} catch (CameraException e) {
logCameraExceptionAndFinish(e);
}
cameraView.setVisibility(INVISIBLE);
statusView.setVisibility(VISIBLE);
status.setText(R.string.connecting_to_device);
} else if (state instanceof CustodianTask.State.SendingShard) {
status.setText("Sending shard");
} 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);
} else if (state instanceof CustodianTask.State.Failure) {
// the activity will replace this fragment with an error fragment
statusView.setVisibility(INVISIBLE);
cameraView.setVisibility(INVISIBLE);
}
}
@Override
public String getUniqueTag() {
return TAG;
}
@UiThread
private void logCameraExceptionAndFinish(CameraException e) {
logException(LOG, WARNING, e);
Toast.makeText(getActivity(), R.string.camera_error,
LENGTH_LONG).show();
finish();
}
@Override
protected void finish() {
requireActivity().getSupportFragmentManager().popBackStack();
}
}

View File

@@ -0,0 +1,110 @@
package org.briarproject.briar.android.socialbackup.recover;
import android.app.Application;
import android.widget.Toast;
import com.google.zxing.Result;
import org.briarproject.bramble.api.UnsupportedVersionException;
import org.briarproject.bramble.api.keyagreement.Payload;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.briar.R;
import org.briarproject.briar.android.contact.add.nearby.QrCodeDecoder;
import org.briarproject.briar.android.viewmodel.LiveEvent;
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
import org.briarproject.briar.api.socialbackup.recovery.CustodianTask;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.cert.CertPathValidatorException;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.inject.Inject;
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import static android.widget.Toast.LENGTH_LONG;
import static java.util.Objects.requireNonNull;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
public class CustodianReturnShardViewModel extends AndroidViewModel
implements QrCodeDecoder.ResultCallback {
private static final Logger LOG =
getLogger(CustodianReturnShardViewModel.class.getName());
private final AndroidExecutor androidExecutor;
private final Executor ioExecutor;
private boolean wasContinueClicked = false;
private final MutableLiveEvent<Boolean> showCameraFragment =
new MutableLiveEvent<>();
private final MutableLiveData<CustodianTask.State> state =
new MutableLiveData<>();
final QrCodeDecoder qrCodeDecoder;
@SuppressWarnings("CharsetObjectCanBeUsed") // Requires minSdkVersion >= 19
private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
@Inject
public CustodianReturnShardViewModel(
@NonNull Application application,
@IoExecutor Executor ioExecutor,
AndroidExecutor androidExecutor) {
super(application);
this.androidExecutor = androidExecutor;
this.ioExecutor = ioExecutor;
qrCodeDecoder = new QrCodeDecoder(androidExecutor, ioExecutor, this);
}
@IoExecutor
@Override
public void onQrCodeDecoded(Result result) {
LOG.info("Got result from decoder");
// Ignore results until the KeyAgreementTask is ready
// if (!gotLocalPayload || gotRemotePayload) return;
try {
byte[] payloadBytes = result.getText().getBytes(ISO_8859_1);
if (LOG.isLoggable(INFO))
LOG.info("Remote payload is " + payloadBytes.length + " bytes");
// Payload remotePayload = payloadParser.parse(payloadBytes);
// gotRemotePayload = true;
// requireNonNull(task).connectAndRunProtocol(remotePayload);
// state.postValue(new ReturnShardState.QrCodeScanned());
} catch (IllegalArgumentException e) {
LOG.log(WARNING, "QR Code Invalid", e);
androidExecutor.runOnUiThread(() -> Toast.makeText(getApplication(),
R.string.qr_code_invalid, LENGTH_LONG).show());
// resetPayloadFlags();
state.postValue(new CustodianTask.State.Failure(
CustodianTask.State.Failure.Reason.QR_CODE_INVALID));
}
}
@UiThread
public void onContinueClicked() {
wasContinueClicked = true;
// checkPermissions.setEvent(true);
showCameraFragment.setEvent(true);
}
QrCodeDecoder getQrCodeDecoder() {
return qrCodeDecoder;
}
LiveEvent<Boolean> getShowCameraFragment() {
return showCameraFragment;
}
LiveData<CustodianTask.State> getState() {
return state;
}
}

View File

@@ -1,70 +0,0 @@
package org.briarproject.briar.android.socialbackup.recover;
import android.os.Bundle;
import android.widget.Toast;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.contact.add.nearby.AddNearbyContactPermissionManager;
import org.briarproject.briar.android.socialbackup.OwnerRecoveryModeMainFragment;
import org.briarproject.briar.api.socialbackup.ReturnShardPayload;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import androidx.lifecycle.ViewModelProvider;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.briar.android.socialbackup.CustodianHelpRecoverActivity.RETURN_SHARD_PAYLOAD;
public class CustodianSendShardActivity extends BriarActivity {
private CustodianSendShardViewModel viewModel;
private static final Logger LOG =
getLogger(CustodianSendShardActivity.class.getName());
@Inject
ViewModelProvider.Factory viewModelFactory;
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
viewModel = new ViewModelProvider(this, viewModelFactory)
.get(CustodianSendViewModel.class);
}
@Override
public void onCreate(@Nullable Bundle state) {
super.onCreate(state);
byte[] returnShardPayloadBytes =
getIntent().getByteArrayExtra(RETURN_SHARD_PAYLOAD);
try {
ReturnShardPayload returnShardPayload = parseReturnShardPayload(
clientHelper.toList(returnShardPayloadBytes));
// viewModel.setReturnShardPayload(returnShardPayload);
} catch (FormatException e) {
Toast.makeText(this,
"Error reading social backup",
Toast.LENGTH_SHORT).show();
finish();
}
setContentView(R.layout.activity_fragment_container);
if (state == null) {
showInitialFragment(new OwnerRecoveryModeExplainerFragment());
}
viewModel.getCheckPermissions().observeEvent(this, check ->
permissionManager.checkPermissions());
viewModel.getRequestBluetoothDiscoverable().observeEvent(this, r ->
requestBluetoothDiscoverable()); // never false
viewModel.getShowQrCodeFragment().observeEvent(this, show -> {
if (show) showQrCodeFragment();
});
viewModel.getState()
.observe(this, this::onReturnShardStateChanged);
}
}

View File

@@ -47,7 +47,6 @@ public class OwnerRecoveryModeExplainerFragment extends BaseFragment {
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
System.out.println("GOt here *************************************");
View view = inflater.inflate(R.layout.fragment_recovery_owner_explainer,
container, false);
Button button = view.findViewById(R.id.beginButton);

View File

@@ -0,0 +1,4 @@
package org.briarproject.briar.socialbackup.recovery;
public class CustodianTaskImpl {
}