From 61453b96abb1b2df3efea4377dd2eb058ce1c2af Mon Sep 17 00:00:00 2001 From: ameba23 Date: Thu, 8 Apr 2021 16:43:39 +0200 Subject: [PATCH] Make a ReturnShardFragment, based on ContactExchangeFragment --- .../android/activity/ActivityComponent.java | 3 + .../contact/add/nearby/CameraException.java | 2 +- .../recover/ReturnShardActivity.java | 6 +- .../recover/ReturnShardFragment.java | 198 ++++++++++++++++++ .../recover/ReturnShardViewModel.java | 6 +- 5 files changed, 209 insertions(+), 6 deletions(-) create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/ReturnShardFragment.java diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java index d3886aa08..92b6c95e2 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java @@ -91,6 +91,7 @@ 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.ReturnShardActivity; +import org.briarproject.briar.android.socialbackup.recover.ReturnShardFragment; import org.briarproject.briar.android.splash.SplashScreenActivity; import org.briarproject.briar.android.test.TestDataActivity; @@ -276,4 +277,6 @@ public interface ActivityComponent { void inject(ExistingBackupFragment existingBackupFragment); void inject(NewOrRecoverFragment newOrRecoverFragment); + + void inject(ReturnShardFragment returnShardFragment); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/CameraException.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/CameraException.java index 04f7a0612..50304e756 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/CameraException.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/CameraException.java @@ -2,7 +2,7 @@ package org.briarproject.briar.android.contact.add.nearby; import java.io.IOException; -class CameraException extends IOException { +public class CameraException extends IOException { CameraException(String message) { super(message); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/ReturnShardActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/ReturnShardActivity.java index ed9b2a7de..975c4bf83 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/ReturnShardActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/ReturnShardActivity.java @@ -14,12 +14,10 @@ import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BaseActivity; import org.briarproject.briar.android.contact.add.nearby.AddNearbyContactErrorFragment; -import org.briarproject.briar.android.contact.add.nearby.AddNearbyContactFragment; import org.briarproject.briar.android.contact.add.nearby.AddNearbyContactPermissionManager; import org.briarproject.briar.android.fragment.BaseFragment; import org.briarproject.briar.android.util.RequestBluetoothDiscoverable; import org.briarproject.briar.api.socialbackup.BackupPayload; -import org.briarproject.briar.api.socialbackup.MessageParser; import org.briarproject.briar.api.socialbackup.ReturnShardPayload; import org.briarproject.briar.api.socialbackup.Shard; @@ -208,8 +206,8 @@ public class ReturnShardActivity extends BaseActivity private void showQrCodeFragment() { // FIXME #824 FragmentManager fm = getSupportFragmentManager(); - if (fm.findFragmentByTag(AddNearbyContactFragment.TAG) == null) { - BaseFragment f = AddNearbyContactFragment.newInstance(); + if (fm.findFragmentByTag(ReturnShardFragment.TAG) == null) { + BaseFragment f = ReturnShardFragment.newInstance(); fm.beginTransaction() .replace(R.id.fragmentContainer, f, f.getUniqueTag()) .addToBackStack(f.getUniqueTag()) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/ReturnShardFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/ReturnShardFragment.java new file mode 100644 index 000000000..c92de05ea --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/ReturnShardFragment.java @@ -0,0 +1,198 @@ +package org.briarproject.briar.android.socialbackup.recover; + +import android.graphics.Bitmap; +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 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 ReturnShardFragment extends BaseFragment + implements QrCodeView.FullscreenListener { + + public static final String TAG = org.briarproject.briar.android.contact.add.nearby.AddNearbyContactFragment.class.getName(); + + private static final Logger LOG = Logger.getLogger(TAG); + + @Inject + ViewModelProvider.Factory viewModelFactory; + + private ReturnShardViewModel viewModel; + private CameraView cameraView; + private LinearLayout cameraOverlay; + private View statusView; + private QrCodeView qrCodeView; + private TextView status; + + public static ReturnShardFragment newInstance() { + Bundle args = new Bundle(); + ReturnShardFragment fragment = new ReturnShardFragment(); + fragment.setArguments(args); + return fragment; + } + + @Override + public void injectFragment(ActivityComponent component) { + component.inject(this); + viewModel = new ViewModelProvider(requireActivity(), viewModelFactory) + .get(ReturnShardViewModel.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); + qrCodeView = view.findViewById(R.id.qr_code_view); + qrCodeView.setFullscreenListener(this); + + 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) { + // Grow the QR code view to fill its parent + statusParams = new LinearLayout.LayoutParams(0, 0, 0f); + qrCodeParams = new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT, 1f); + } else { + // Shrink the QR code view to fill half its parent + if (cameraOverlay.getOrientation() == HORIZONTAL) { + statusParams = new LinearLayout.LayoutParams(0, MATCH_PARENT, 1f); + qrCodeParams = new LinearLayout.LayoutParams(0, MATCH_PARENT, 1f); + } else { + statusParams = new LinearLayout.LayoutParams(MATCH_PARENT, 0, 1f); + qrCodeParams = new LinearLayout.LayoutParams(MATCH_PARENT, 0, 1f); + } + } + statusView.setLayoutParams(statusParams); + qrCodeView.setLayoutParams(qrCodeParams); + cameraOverlay.invalidate(); + } + + @UiThread + private void onReturnShardStateChanged(@Nullable ReturnShardState state) { + if (state instanceof ReturnShardState.KeyAgreementListening) { + Bitmap qrCode = + ((ReturnShardState.KeyAgreementListening) state).qrCode; + qrCodeView.setQrCode(qrCode); + } else if (state instanceof ReturnShardState.QrCodeScanned) { + 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 ReturnShardState.KeyAgreementWaiting) { + status.setText(R.string.waiting_for_contact_to_scan); + } else if (state instanceof ReturnShardState.KeyAgreementStarted) { + qrCodeView.setVisibility(INVISIBLE); + status.setText(R.string.authenticating_with_device); + } else if (state instanceof ReturnShardState.SocialBackupExchangeStarted) { + status.setText(R.string.exchanging_contact_details); + } else if (state instanceof ReturnShardState.Failed) { + // 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(); + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/ReturnShardViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/ReturnShardViewModel.java index 932189098..3e4576d8e 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/ReturnShardViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/ReturnShardViewModel.java @@ -410,7 +410,7 @@ class ReturnShardViewModel extends AndroidViewModel @Override @IoExecutor public void onQrCodeDecoded(Result result) { - LOG.info("Got result from decoder"); + LOG.info("Got result from decoder"+gotLocalPayload+gotRemotePayload); // Ignore results until the KeyAgreementTask is ready if (!gotLocalPayload || gotRemotePayload) return; try { @@ -538,4 +538,8 @@ class ReturnShardViewModel extends AndroidViewModel public void setReturnShardPayload(ReturnShardPayload returnShardPayload) { this.returnShardPayload = returnShardPayload; } + + QrCodeDecoder getQrCodeDecoder() { + return qrCodeDecoder; + } }