mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-20 14:49:53 +01:00
WIP make part of keyAgreement public, so it can be used for shard return
This commit is contained in:
@@ -87,6 +87,7 @@ import org.briarproject.briar.android.socialbackup.DistributedBackupActivity;
|
|||||||
import org.briarproject.briar.android.socialbackup.ExistingBackupFragment;
|
import org.briarproject.briar.android.socialbackup.ExistingBackupFragment;
|
||||||
import org.briarproject.briar.android.socialbackup.OwnerRecoveryModeExplainerFragment;
|
import org.briarproject.briar.android.socialbackup.OwnerRecoveryModeExplainerFragment;
|
||||||
import org.briarproject.briar.android.socialbackup.RecoverActivity;
|
import org.briarproject.briar.android.socialbackup.RecoverActivity;
|
||||||
|
import org.briarproject.briar.android.socialbackup.ShardQrCodeFragment;
|
||||||
import org.briarproject.briar.android.socialbackup.ShardsSentFragment;
|
import org.briarproject.briar.android.socialbackup.ShardsSentFragment;
|
||||||
import org.briarproject.briar.android.socialbackup.ThresholdSelectorFragment;
|
import org.briarproject.briar.android.socialbackup.ThresholdSelectorFragment;
|
||||||
import org.briarproject.briar.android.socialbackup.creation.CreateBackupModule;
|
import org.briarproject.briar.android.socialbackup.creation.CreateBackupModule;
|
||||||
@@ -258,6 +259,8 @@ public interface ActivityComponent {
|
|||||||
|
|
||||||
void inject(ThresholdSelectorFragment thresholdSelectorFragment);
|
void inject(ThresholdSelectorFragment thresholdSelectorFragment);
|
||||||
|
|
||||||
|
void inject(ShardQrCodeFragment shardQrCodeFragment);
|
||||||
|
|
||||||
void inject(DistributedBackupActivity distributedBackupActivity);
|
void inject(DistributedBackupActivity distributedBackupActivity);
|
||||||
|
|
||||||
void inject(RecoverActivity recoverActivity);
|
void inject(RecoverActivity recoverActivity);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package org.briarproject.briar.android.keyagreement;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
class CameraException extends IOException {
|
public class CameraException extends IOException {
|
||||||
|
|
||||||
CameraException(String message) {
|
CameraException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import static java.util.logging.Level.WARNING;
|
|||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(QrCodeDecoder.class.getName());
|
Logger.getLogger(QrCodeDecoder.class.getName());
|
||||||
@@ -41,7 +41,7 @@ class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
|||||||
private Camera camera = null;
|
private Camera camera = null;
|
||||||
private int cameraIndex = 0;
|
private int cameraIndex = 0;
|
||||||
|
|
||||||
QrCodeDecoder(ResultCallback callback) {
|
public QrCodeDecoder(ResultCallback callback) {
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
interface ResultCallback {
|
public interface ResultCallback {
|
||||||
|
|
||||||
void handleResult(Result result);
|
void handleResult(Result result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,13 +21,13 @@ import static java.util.logging.Level.WARNING;
|
|||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class QrCodeUtils {
|
public class QrCodeUtils {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(QrCodeUtils.class.getName());
|
Logger.getLogger(QrCodeUtils.class.getName());
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
static Bitmap createQrCode(DisplayMetrics dm, String input) {
|
public static Bitmap createQrCode(DisplayMetrics dm, String input) {
|
||||||
int smallestDimen = Math.min(dm.widthPixels, dm.heightPixels);
|
int smallestDimen = Math.min(dm.widthPixels, dm.heightPixels);
|
||||||
try {
|
try {
|
||||||
// Generate QR code
|
// Generate QR code
|
||||||
|
|||||||
@@ -3,14 +3,59 @@ package org.briarproject.briar.android.socialbackup;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementResult;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.activity.BaseActivity;
|
import org.briarproject.briar.android.activity.BaseActivity;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import androidx.annotation.UiThread;
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
|
||||||
|
import static android.Manifest.permission.CAMERA;
|
||||||
|
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||||
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA_LOCATION;
|
||||||
|
|
||||||
public class RecoverActivity extends BaseActivity implements
|
public class RecoverActivity extends BaseActivity implements
|
||||||
BaseFragment.BaseFragmentListener, ExplainerDismissedListener,
|
BaseFragment.BaseFragmentListener, ExplainerDismissedListener,
|
||||||
ScanQrButtonListener {
|
ScanQrButtonListener, ShardQrCodeFragment.ShardQrCodeEventListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keyAgreementFailed() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String keyAgreementWaiting() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String keyAgreementStarted() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keyAgreementAborted(boolean remoteAborted) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String keyAgreementFinished(KeyAgreementResult result) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum Permission {
|
||||||
|
UNKNOWN, GRANTED, SHOW_RATIONALE, PERMANENTLY_DENIED
|
||||||
|
}
|
||||||
|
|
||||||
|
private Permission cameraPermission = Permission.UNKNOWN;
|
||||||
|
|
||||||
|
|
||||||
private int numRecovered;
|
private int numRecovered;
|
||||||
|
|
||||||
@@ -47,11 +92,79 @@ public class RecoverActivity extends BaseActivity implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void scanQrButtonClicked() {
|
public void scanQrButtonClicked() {
|
||||||
// TODO
|
if (checkPermissions()) showQrCodeFragment();
|
||||||
Toast.makeText(this,
|
}
|
||||||
"coming soon...",
|
|
||||||
Toast.LENGTH_SHORT).show();
|
|
||||||
finish();
|
private void showQrCodeFragment() {
|
||||||
|
ShardQrCodeFragment f = ShardQrCodeFragment.newInstance();
|
||||||
|
showNextFragment(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void requestPermissions() {
|
||||||
|
String[] permissions = new String[] {CAMERA};
|
||||||
|
ActivityCompat.requestPermissions(this, permissions,
|
||||||
|
REQUEST_PERMISSION_CAMERA_LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@UiThread
|
||||||
|
public void onRequestPermissionsResult(int requestCode,
|
||||||
|
String[] permissions, int[] grantResults) {
|
||||||
|
if (requestCode != REQUEST_PERMISSION_CAMERA_LOCATION)
|
||||||
|
throw new AssertionError();
|
||||||
|
if (gotPermission(CAMERA, permissions, grantResults)) {
|
||||||
|
cameraPermission = Permission.GRANTED;
|
||||||
|
} else if (shouldShowRationale(CAMERA)) {
|
||||||
|
cameraPermission = Permission.SHOW_RATIONALE;
|
||||||
|
} else {
|
||||||
|
cameraPermission = Permission.PERMANENTLY_DENIED;
|
||||||
|
}
|
||||||
|
// If a permission dialog has been shown, showing the QR code fragment
|
||||||
|
// on this call path would cause a crash due to
|
||||||
|
// https://code.google.com/p/android/issues/detail?id=190966.
|
||||||
|
// In that case the isResumed flag prevents the fragment from being
|
||||||
|
// shown here, and showQrCodeFragmentIfAllowed() will be called again
|
||||||
|
// from onPostResume().
|
||||||
|
if (checkPermissions()) showQrCodeFragment();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean gotPermission(String permission, String[] permissions,
|
||||||
|
int[] grantResults) {
|
||||||
|
for (int i = 0; i < permissions.length; i++) {
|
||||||
|
if (permission.equals(permissions[i]))
|
||||||
|
return grantResults[i] == PERMISSION_GRANTED;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldShowRationale(String permission) {
|
||||||
|
return ActivityCompat.shouldShowRequestPermissionRationale(this,
|
||||||
|
permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkPermissions() {
|
||||||
|
if (areEssentialPermissionsGranted()) return true;
|
||||||
|
// If an essential permission has been permanently denied, ask the
|
||||||
|
// user to change the setting
|
||||||
|
if (cameraPermission == Permission.PERMANENTLY_DENIED) {
|
||||||
|
Toast.makeText(this,
|
||||||
|
"camera permission is denied",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
// showDenialDialog(R.string.permission_camera_title,
|
||||||
|
// R.string.permission_camera_denied_body);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cameraPermission == Permission.SHOW_RATIONALE) {
|
||||||
|
// showRationale(R.string.permission_camera_title,
|
||||||
|
// R.string.permission_camera_request_body);
|
||||||
|
Toast.makeText(this,
|
||||||
|
"camera permission - show rationale",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
requestPermissions();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -59,4 +172,8 @@ public class RecoverActivity extends BaseActivity implements
|
|||||||
public void runOnDbThread(Runnable runnable) {
|
public void runOnDbThread(Runnable runnable) {
|
||||||
throw new RuntimeException("Don't use this deprecated method here.");
|
throw new RuntimeException("Don't use this deprecated method here.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean areEssentialPermissionsGranted() {
|
||||||
|
return cameraPermission == Permission.GRANTED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,381 @@
|
|||||||
|
package org.briarproject.briar.android.socialbackup;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
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 com.google.zxing.Result;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.UnsupportedVersionException;
|
||||||
|
import org.briarproject.bramble.api.event.Event;
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementResult;
|
||||||
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
|
||||||
|
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||||
|
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||||
|
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
||||||
|
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementAbortedEvent;
|
||||||
|
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFailedEvent;
|
||||||
|
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFinishedEvent;
|
||||||
|
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
|
||||||
|
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStartedEvent;
|
||||||
|
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementWaitingEvent;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
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.BaseEventFragment;
|
||||||
|
import org.briarproject.briar.android.keyagreement.CameraException;
|
||||||
|
import org.briarproject.briar.android.keyagreement.CameraView;
|
||||||
|
import org.briarproject.briar.android.keyagreement.ContactExchangeErrorFragment;
|
||||||
|
import org.briarproject.briar.android.keyagreement.QrCodeDecoder;
|
||||||
|
import org.briarproject.briar.android.keyagreement.QrCodeUtils;
|
||||||
|
import org.briarproject.briar.android.view.QrCodeView;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Provider;
|
||||||
|
|
||||||
|
import androidx.annotation.UiThread;
|
||||||
|
|
||||||
|
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
|
||||||
|
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.INFO;
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersNotNullByDefault
|
||||||
|
public class ShardQrCodeFragment extends BaseEventFragment
|
||||||
|
implements QrCodeDecoder.ResultCallback, QrCodeView.FullscreenListener {
|
||||||
|
|
||||||
|
static final String TAG = org.briarproject.briar.android.keyagreement.KeyAgreementFragment.class.getName();
|
||||||
|
|
||||||
|
private static final Logger LOG = Logger.getLogger(TAG);
|
||||||
|
@SuppressWarnings("CharsetObjectCanBeUsed") // Requires minSdkVersion >= 19
|
||||||
|
private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
Provider<KeyAgreementTask> keyAgreementTaskProvider;
|
||||||
|
@Inject
|
||||||
|
PayloadEncoder payloadEncoder;
|
||||||
|
@Inject
|
||||||
|
PayloadParser payloadParser;
|
||||||
|
@Inject
|
||||||
|
@IoExecutor
|
||||||
|
Executor ioExecutor;
|
||||||
|
@Inject
|
||||||
|
EventBus eventBus;
|
||||||
|
|
||||||
|
private CameraView cameraView;
|
||||||
|
private LinearLayout cameraOverlay;
|
||||||
|
private View statusView;
|
||||||
|
private QrCodeView qrCodeView;
|
||||||
|
private TextView status;
|
||||||
|
|
||||||
|
private boolean gotRemotePayload;
|
||||||
|
private volatile boolean gotLocalPayload;
|
||||||
|
private KeyAgreementTask task;
|
||||||
|
private ShardQrCodeEventListener
|
||||||
|
listener;
|
||||||
|
|
||||||
|
public static ShardQrCodeFragment newInstance() {
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
ShardQrCodeFragment
|
||||||
|
fragment = new ShardQrCodeFragment();
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
listener = (ShardQrCodeEventListener) context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectFragment(ActivityComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUniqueTag() {
|
||||||
|
return TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
requireActivity().setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR);
|
||||||
|
cameraView.setPreviewConsumer(new QrCodeDecoder(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
try {
|
||||||
|
cameraView.start();
|
||||||
|
} catch (CameraException e) {
|
||||||
|
logCameraExceptionAndFinish(e);
|
||||||
|
}
|
||||||
|
startListening();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
stopListening();
|
||||||
|
try {
|
||||||
|
cameraView.stop();
|
||||||
|
} catch (CameraException e) {
|
||||||
|
logCameraExceptionAndFinish(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private void logCameraExceptionAndFinish(CameraException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
Toast.makeText(getActivity(), R.string.camera_error,
|
||||||
|
LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private void startListening() {
|
||||||
|
KeyAgreementTask oldTask = task;
|
||||||
|
KeyAgreementTask newTask = keyAgreementTaskProvider.get();
|
||||||
|
task = newTask;
|
||||||
|
ioExecutor.execute(() -> {
|
||||||
|
if (oldTask != null) oldTask.stopListening();
|
||||||
|
newTask.listen();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private void stopListening() {
|
||||||
|
KeyAgreementTask oldTask = task;
|
||||||
|
ioExecutor.execute(() -> {
|
||||||
|
if (oldTask != null) oldTask.stopListening();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private void reset() {
|
||||||
|
// If we've stopped the camera view, restart it
|
||||||
|
if (gotRemotePayload) {
|
||||||
|
try {
|
||||||
|
cameraView.start();
|
||||||
|
} catch (CameraException e) {
|
||||||
|
logCameraExceptionAndFinish(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
statusView.setVisibility(INVISIBLE);
|
||||||
|
cameraView.setVisibility(VISIBLE);
|
||||||
|
gotRemotePayload = false;
|
||||||
|
gotLocalPayload = false;
|
||||||
|
startListening();
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private void qrCodeScanned(String content) {
|
||||||
|
try {
|
||||||
|
byte[] payloadBytes = content.getBytes(ISO_8859_1);
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Remote payload is " + payloadBytes.length + " bytes");
|
||||||
|
Payload remotePayload = payloadParser.parse(payloadBytes);
|
||||||
|
gotRemotePayload = true;
|
||||||
|
cameraView.stop();
|
||||||
|
cameraView.setVisibility(INVISIBLE);
|
||||||
|
statusView.setVisibility(VISIBLE);
|
||||||
|
status.setText(R.string.connecting_to_device);
|
||||||
|
task.connectAndRunProtocol(remotePayload);
|
||||||
|
} catch (UnsupportedVersionException e) {
|
||||||
|
reset();
|
||||||
|
String msg;
|
||||||
|
if (e.isTooOld()) {
|
||||||
|
msg = getString(R.string.qr_code_too_old,
|
||||||
|
getString(R.string.app_name));
|
||||||
|
} else {
|
||||||
|
msg = getString(R.string.qr_code_too_new,
|
||||||
|
getString(R.string.app_name));
|
||||||
|
}
|
||||||
|
showNextFragment(ContactExchangeErrorFragment.newInstance(msg));
|
||||||
|
} catch (CameraException e) {
|
||||||
|
logCameraExceptionAndFinish(e);
|
||||||
|
} catch (IOException | IllegalArgumentException e) {
|
||||||
|
LOG.log(WARNING, "QR Code Invalid", e);
|
||||||
|
reset();
|
||||||
|
Toast.makeText(getActivity(), R.string.qr_code_invalid,
|
||||||
|
LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void eventOccurred(Event e) {
|
||||||
|
if (e instanceof KeyAgreementListeningEvent) {
|
||||||
|
KeyAgreementListeningEvent event = (KeyAgreementListeningEvent) e;
|
||||||
|
gotLocalPayload = true;
|
||||||
|
setQrCode(event.getLocalPayload());
|
||||||
|
} else if (e instanceof KeyAgreementFailedEvent) {
|
||||||
|
keyAgreementFailed();
|
||||||
|
} else if (e instanceof KeyAgreementWaitingEvent) {
|
||||||
|
keyAgreementWaiting();
|
||||||
|
} else if (e instanceof KeyAgreementStartedEvent) {
|
||||||
|
keyAgreementStarted();
|
||||||
|
} else if (e instanceof KeyAgreementAbortedEvent) {
|
||||||
|
KeyAgreementAbortedEvent event = (KeyAgreementAbortedEvent) e;
|
||||||
|
keyAgreementAborted(event.didRemoteAbort());
|
||||||
|
} else if (e instanceof KeyAgreementFinishedEvent) {
|
||||||
|
keyAgreementFinished(((KeyAgreementFinishedEvent) e).getResult());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private void keyAgreementFailed() {
|
||||||
|
reset();
|
||||||
|
listener.keyAgreementFailed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private void keyAgreementWaiting() {
|
||||||
|
status.setText(listener.keyAgreementWaiting());
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private void keyAgreementStarted() {
|
||||||
|
qrCodeView.setVisibility(INVISIBLE);
|
||||||
|
statusView.setVisibility(VISIBLE);
|
||||||
|
status.setText(listener.keyAgreementStarted());
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private void keyAgreementAborted(boolean remoteAborted) {
|
||||||
|
reset();
|
||||||
|
listener.keyAgreementAborted(remoteAborted);
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private void keyAgreementFinished(KeyAgreementResult result) {
|
||||||
|
statusView.setVisibility(VISIBLE);
|
||||||
|
status.setText(listener.keyAgreementFinished(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setQrCode(Payload localPayload) {
|
||||||
|
Context context = getContext();
|
||||||
|
if (context == null) return;
|
||||||
|
DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
||||||
|
ioExecutor.execute(() -> {
|
||||||
|
byte[] payloadBytes = payloadEncoder.encode(localPayload);
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("Local payload is " + payloadBytes.length
|
||||||
|
+ " bytes");
|
||||||
|
}
|
||||||
|
// Use ISO 8859-1 to encode bytes directly as a string
|
||||||
|
String content = new String(payloadBytes, ISO_8859_1);
|
||||||
|
Bitmap qrCode = QrCodeUtils.createQrCode(dm, content);
|
||||||
|
runOnUiThreadUnlessDestroyed(() -> qrCodeView.setQrCode(qrCode));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleResult(Result result) {
|
||||||
|
runOnUiThreadUnlessDestroyed(() -> {
|
||||||
|
LOG.info("Got result from decoder");
|
||||||
|
// Ignore results until the KeyAgreementTask is ready
|
||||||
|
if (!gotLocalPayload) return;
|
||||||
|
if (!gotRemotePayload) qrCodeScanned(result.getText());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finish() {
|
||||||
|
getActivity().getSupportFragmentManager().popBackStack();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
interface ShardQrCodeEventListener {
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
void keyAgreementFailed();
|
||||||
|
|
||||||
|
// Should return a string to be displayed as status.
|
||||||
|
@UiThread
|
||||||
|
@Nullable
|
||||||
|
String keyAgreementWaiting();
|
||||||
|
|
||||||
|
// Should return a string to be displayed as status.
|
||||||
|
@UiThread
|
||||||
|
@Nullable
|
||||||
|
String keyAgreementStarted();
|
||||||
|
|
||||||
|
// Will show an error fragment.
|
||||||
|
@UiThread
|
||||||
|
void keyAgreementAborted(boolean remoteAborted);
|
||||||
|
|
||||||
|
// Should return a string to be displayed as status.
|
||||||
|
@UiThread
|
||||||
|
@Nullable
|
||||||
|
String keyAgreementFinished(KeyAgreementResult result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package org.briarproject.briar.api.keyagreement;
|
||||||
|
|
||||||
|
public class KeyAgreementUtils {
|
||||||
|
|
||||||
|
public enum BluetoothDecision {
|
||||||
|
/**
|
||||||
|
* We haven't asked the user about Bluetooth discoverability.
|
||||||
|
*/
|
||||||
|
UNKNOWN,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The device doesn't have a Bluetooth adapter.
|
||||||
|
*/
|
||||||
|
NO_ADAPTER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We're waiting for the user to accept or refuse discoverability.
|
||||||
|
*/
|
||||||
|
WAITING,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user has accepted discoverability.
|
||||||
|
*/
|
||||||
|
ACCEPTED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user has refused discoverability.
|
||||||
|
*/
|
||||||
|
REFUSED
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Permission {
|
||||||
|
UNKNOWN, GRANTED, SHOW_RATIONALE, PERMANENTLY_DENIED
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user