mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-21 07:09:56 +01:00
Scan Mailbox QR code for setup and show progress screen
This commit is contained in:
@@ -44,6 +44,7 @@ import org.briarproject.briar.android.hotspot.ManualHotspotFragment;
|
|||||||
import org.briarproject.briar.android.hotspot.QrHotspotFragment;
|
import org.briarproject.briar.android.hotspot.QrHotspotFragment;
|
||||||
import org.briarproject.briar.android.logging.CachingLogHandler;
|
import org.briarproject.briar.android.logging.CachingLogHandler;
|
||||||
import org.briarproject.briar.android.login.SignInReminderReceiver;
|
import org.briarproject.briar.android.login.SignInReminderReceiver;
|
||||||
|
import org.briarproject.briar.android.mailbox.MailboxScanFragment;
|
||||||
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;
|
||||||
@@ -239,4 +240,6 @@ public interface AndroidComponent
|
|||||||
void inject(ReceiveFragment receiveFragment);
|
void inject(ReceiveFragment receiveFragment);
|
||||||
|
|
||||||
void inject(BluetoothIntroFragment bluetoothIntroFragment);
|
void inject(BluetoothIntroFragment bluetoothIntroFragment);
|
||||||
|
|
||||||
|
void inject(MailboxScanFragment mailboxScanFragment);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,10 @@ package org.briarproject.briar.android.contact.add.nearby;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.util.Permission;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import androidx.annotation.StringRes;
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
import androidx.core.util.Consumer;
|
import androidx.core.util.Consumer;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
@@ -17,16 +16,13 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
|||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale;
|
import static androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale;
|
||||||
import static androidx.core.content.ContextCompat.checkSelfPermission;
|
import static androidx.core.content.ContextCompat.checkSelfPermission;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.getGoToSettingsListener;
|
|
||||||
import static org.briarproject.briar.android.util.UiUtils.isLocationEnabled;
|
import static org.briarproject.briar.android.util.UiUtils.isLocationEnabled;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.showDenialDialog;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.showLocationDialog;
|
import static org.briarproject.briar.android.util.UiUtils.showLocationDialog;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.showRationale;
|
||||||
|
|
||||||
class AddNearbyContactPermissionManager {
|
class AddNearbyContactPermissionManager {
|
||||||
|
|
||||||
private enum Permission {
|
|
||||||
UNKNOWN, GRANTED, SHOW_RATIONALE, PERMANENTLY_DENIED
|
|
||||||
}
|
|
||||||
|
|
||||||
private Permission cameraPermission = Permission.UNKNOWN;
|
private Permission cameraPermission = Permission.UNKNOWN;
|
||||||
private Permission locationPermission = Permission.UNKNOWN;
|
private Permission locationPermission = Permission.UNKNOWN;
|
||||||
|
|
||||||
@@ -68,27 +64,30 @@ class AddNearbyContactPermissionManager {
|
|||||||
// If an essential permission has been permanently denied, ask the
|
// If an essential permission has been permanently denied, ask the
|
||||||
// user to change the setting
|
// user to change the setting
|
||||||
if (cameraPermission == Permission.PERMANENTLY_DENIED) {
|
if (cameraPermission == Permission.PERMANENTLY_DENIED) {
|
||||||
showDenialDialog(R.string.permission_camera_title,
|
showDenialDialog(ctx, R.string.permission_camera_title,
|
||||||
R.string.permission_camera_denied_body);
|
R.string.permission_camera_denied_body);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (isBluetoothSupported &&
|
if (isBluetoothSupported &&
|
||||||
locationPermission == Permission.PERMANENTLY_DENIED) {
|
locationPermission == Permission.PERMANENTLY_DENIED) {
|
||||||
showDenialDialog(R.string.permission_location_title,
|
showDenialDialog(ctx, R.string.permission_location_title,
|
||||||
R.string.permission_location_denied_body);
|
R.string.permission_location_denied_body);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Should we show the rationale for one or both permissions?
|
// Should we show the rationale for one or both permissions?
|
||||||
if (cameraPermission == Permission.SHOW_RATIONALE &&
|
if (cameraPermission == Permission.SHOW_RATIONALE &&
|
||||||
locationPermission == Permission.SHOW_RATIONALE) {
|
locationPermission == Permission.SHOW_RATIONALE) {
|
||||||
showRationale(R.string.permission_camera_location_title,
|
showRationale(ctx, R.string.permission_camera_location_title,
|
||||||
R.string.permission_camera_location_request_body);
|
R.string.permission_camera_location_request_body,
|
||||||
|
this::requestPermissions);
|
||||||
} else if (cameraPermission == Permission.SHOW_RATIONALE) {
|
} else if (cameraPermission == Permission.SHOW_RATIONALE) {
|
||||||
showRationale(R.string.permission_camera_title,
|
showRationale(ctx, R.string.permission_camera_title,
|
||||||
R.string.permission_camera_request_body);
|
R.string.permission_camera_request_body,
|
||||||
|
this::requestPermissions);
|
||||||
} else if (locationPermission == Permission.SHOW_RATIONALE) {
|
} else if (locationPermission == Permission.SHOW_RATIONALE) {
|
||||||
showRationale(R.string.permission_location_title,
|
showRationale(ctx, R.string.permission_location_title,
|
||||||
R.string.permission_location_request_body);
|
R.string.permission_location_request_body,
|
||||||
|
this::requestPermissions);
|
||||||
} else if (locationEnabled) {
|
} else if (locationEnabled) {
|
||||||
requestPermissions();
|
requestPermissions();
|
||||||
} else {
|
} else {
|
||||||
@@ -97,27 +96,6 @@ class AddNearbyContactPermissionManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showDenialDialog(@StringRes int title, @StringRes int body) {
|
|
||||||
AlertDialog.Builder builder =
|
|
||||||
new AlertDialog.Builder(ctx, R.style.BriarDialogTheme);
|
|
||||||
builder.setTitle(title);
|
|
||||||
builder.setMessage(body);
|
|
||||||
builder.setPositiveButton(R.string.ok, getGoToSettingsListener(ctx));
|
|
||||||
builder.setNegativeButton(R.string.cancel,
|
|
||||||
(dialog, which) -> ctx.supportFinishAfterTransition());
|
|
||||||
builder.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showRationale(@StringRes int title, @StringRes int body) {
|
|
||||||
AlertDialog.Builder builder =
|
|
||||||
new AlertDialog.Builder(ctx, R.style.BriarDialogTheme);
|
|
||||||
builder.setTitle(title);
|
|
||||||
builder.setMessage(body);
|
|
||||||
builder.setNeutralButton(R.string.continue_button,
|
|
||||||
(dialog, which) -> requestPermissions());
|
|
||||||
builder.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void requestPermissions() {
|
private void requestPermissions() {
|
||||||
String[] permissions;
|
String[] permissions;
|
||||||
if (isBluetoothSupported) {
|
if (isBluetoothSupported) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.app.Activity;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.util.Permission;
|
||||||
|
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -19,10 +20,6 @@ import static org.briarproject.briar.android.util.UiUtils.showLocationDialog;
|
|||||||
|
|
||||||
class BluetoothConditionManager {
|
class BluetoothConditionManager {
|
||||||
|
|
||||||
private enum Permission {
|
|
||||||
UNKNOWN, GRANTED, SHOW_RATIONALE, PERMANENTLY_DENIED
|
|
||||||
}
|
|
||||||
|
|
||||||
private Permission locationPermission = Permission.UNKNOWN;
|
private Permission locationPermission = Permission.UNKNOWN;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -20,11 +20,7 @@ import static android.content.Context.WIFI_SERVICE;
|
|||||||
*/
|
*/
|
||||||
abstract class AbstractConditionManager {
|
abstract class AbstractConditionManager {
|
||||||
|
|
||||||
enum Permission {
|
final Consumer<Boolean> permissionUpdateCallback;
|
||||||
UNKNOWN, GRANTED, SHOW_RATIONALE, PERMANENTLY_DENIED
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final Consumer<Boolean> permissionUpdateCallback;
|
|
||||||
protected FragmentActivity ctx;
|
protected FragmentActivity ctx;
|
||||||
WifiManager wifiManager;
|
WifiManager wifiManager;
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.content.Intent;
|
|||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.util.Permission;
|
||||||
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package org.briarproject.briar.android.mailbox;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.util.Permission;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import androidx.core.util.Consumer;
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
|
import static android.Manifest.permission.CAMERA;
|
||||||
|
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||||
|
import static androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale;
|
||||||
|
import static androidx.core.content.ContextCompat.checkSelfPermission;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.showDenialDialog;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.showRationale;
|
||||||
|
|
||||||
|
class CameraPermissionManager {
|
||||||
|
|
||||||
|
private Permission cameraPermission = Permission.UNKNOWN;
|
||||||
|
|
||||||
|
private final FragmentActivity ctx;
|
||||||
|
private final Consumer<String[]> requestPermissions;
|
||||||
|
|
||||||
|
CameraPermissionManager(FragmentActivity ctx,
|
||||||
|
Consumer<String[]> requestPermissions) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.requestPermissions = requestPermissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetPermissions() {
|
||||||
|
cameraPermission = Permission.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean areEssentialPermissionsGranted(Context ctx) {
|
||||||
|
return checkSelfPermission(ctx, CAMERA) == PERMISSION_GRANTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean areEssentialPermissionsGranted() {
|
||||||
|
return cameraPermission == Permission.GRANTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
showDenialDialog(ctx, R.string.permission_camera_title,
|
||||||
|
R.string.permission_camera_qr_denied_body);
|
||||||
|
} else if (cameraPermission == Permission.SHOW_RATIONALE) {
|
||||||
|
showRationale(ctx, R.string.permission_camera_title,
|
||||||
|
R.string.permission_camera_request_body,
|
||||||
|
this::requestPermissions);
|
||||||
|
} else {
|
||||||
|
requestPermissions();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void requestPermissions() {
|
||||||
|
String[] permissions = new String[] {CAMERA};
|
||||||
|
requestPermissions.accept(permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onRequestPermissionResult(Map<String, Boolean> result) {
|
||||||
|
if (gotPermission(result)) {
|
||||||
|
cameraPermission = Permission.GRANTED;
|
||||||
|
} else if (shouldShowRequestPermissionRationale(ctx, CAMERA)) {
|
||||||
|
cameraPermission = Permission.SHOW_RATIONALE;
|
||||||
|
} else {
|
||||||
|
cameraPermission = Permission.PERMANENTLY_DENIED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean gotPermission(Map<String, Boolean> result) {
|
||||||
|
Boolean permissionResult = result.get(CAMERA);
|
||||||
|
return permissionResult == null ? areEssentialPermissionsGranted(ctx) :
|
||||||
|
permissionResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ import androidx.lifecycle.ViewModelProvider;
|
|||||||
|
|
||||||
import static android.view.View.INVISIBLE;
|
import static android.view.View.INVISIBLE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.showFragment;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -46,13 +47,13 @@ public class MailboxActivity extends BriarActivity {
|
|||||||
progressBar.setVisibility(VISIBLE);
|
progressBar.setVisibility(VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
viewModel.getState().observe(this, state -> {
|
||||||
viewModel.getState().observe(this, state -> {
|
if (state instanceof MailboxState.NotSetup) {
|
||||||
if (state instanceof MailboxState.NotSetup) {
|
if (savedInstanceState == null) onNotSetup();
|
||||||
onNotSetup();
|
} else if (state instanceof MailboxState.SettingUp) {
|
||||||
}
|
onCodeScanned();
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -64,6 +65,17 @@ public class MailboxActivity extends BriarActivity {
|
|||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
if (viewModel.getState()
|
||||||
|
.getValue() instanceof MailboxState.SettingUp) {
|
||||||
|
// don't go back in flow if we are already setting up mailbox
|
||||||
|
supportFinishAfterTransition();
|
||||||
|
} else {
|
||||||
|
super.onBackPressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void onNotSetup() {
|
private void onNotSetup() {
|
||||||
progressBar.setVisibility(INVISIBLE);
|
progressBar.setVisibility(INVISIBLE);
|
||||||
getSupportFragmentManager().beginTransaction()
|
getSupportFragmentManager().beginTransaction()
|
||||||
@@ -72,4 +84,10 @@ public class MailboxActivity extends BriarActivity {
|
|||||||
.commit();
|
.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onCodeScanned() {
|
||||||
|
showFragment(getSupportFragmentManager(),
|
||||||
|
new MailboxConnectingFragment(),
|
||||||
|
MailboxConnectingFragment.TAG, false);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package org.briarproject.briar.android.mailbox;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersNotNullByDefault
|
||||||
|
public class MailboxConnectingFragment extends Fragment {
|
||||||
|
|
||||||
|
static final String TAG = MailboxConnectingFragment.class.getName();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater,
|
||||||
|
@Nullable ViewGroup container,
|
||||||
|
@Nullable Bundle savedInstanceState) {
|
||||||
|
return inflater.inflate(R.layout.fragment_mailbox_connecting,
|
||||||
|
container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
requireActivity().setTitle(R.string.mailbox_setup_title);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package org.briarproject.briar.android.mailbox;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
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.qrcode.CameraException;
|
||||||
|
import org.briarproject.briar.android.qrcode.CameraView;
|
||||||
|
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import androidx.annotation.UiThread;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
|
import static android.widget.Toast.LENGTH_LONG;
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
||||||
|
|
||||||
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersNotNullByDefault
|
||||||
|
public class MailboxScanFragment extends Fragment {
|
||||||
|
|
||||||
|
static final String TAG = MailboxScanFragment.class.getName();
|
||||||
|
|
||||||
|
private static final Logger LOG = Logger.getLogger(TAG);
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ViewModelProvider.Factory viewModelFactory;
|
||||||
|
|
||||||
|
private MailboxViewModel viewModel;
|
||||||
|
|
||||||
|
private CameraView cameraView;
|
||||||
|
|
||||||
|
@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
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater,
|
||||||
|
@Nullable ViewGroup container,
|
||||||
|
@Nullable Bundle savedInstanceState) {
|
||||||
|
return inflater.inflate(R.layout.fragment_mailbox_scan, container,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
cameraView = view.findViewById(R.id.camera_view);
|
||||||
|
cameraView.setPreviewConsumer(viewModel.getQrCodeDecoder());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
requireActivity().setTitle(R.string.mailbox_setup_button_scan);
|
||||||
|
try {
|
||||||
|
cameraView.start();
|
||||||
|
} catch (CameraException e) {
|
||||||
|
logCameraExceptionAndFinish(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
try {
|
||||||
|
cameraView.stop();
|
||||||
|
} catch (CameraException e) {
|
||||||
|
logCameraExceptionAndFinish(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private void logCameraExceptionAndFinish(CameraException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
Toast.makeText(requireContext(), R.string.camera_error,
|
||||||
|
LENGTH_LONG).show();
|
||||||
|
requireActivity().getSupportFragmentManager().popBackStack();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,6 +5,9 @@ class MailboxState {
|
|||||||
static class NotSetup extends MailboxState {
|
static class NotSetup extends MailboxState {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class SettingUp extends MailboxState {
|
||||||
|
}
|
||||||
|
|
||||||
// TODO add other states
|
// TODO add other states
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ import org.briarproject.bramble.api.db.DatabaseExecutor;
|
|||||||
import org.briarproject.bramble.api.db.TransactionManager;
|
import org.briarproject.bramble.api.db.TransactionManager;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
|
||||||
import org.briarproject.briar.android.mailbox.MailboxState.NotSetup;
|
import org.briarproject.briar.android.mailbox.MailboxState.NotSetup;
|
||||||
import org.briarproject.briar.android.qrcode.QrCodeDecoder;
|
import org.briarproject.briar.android.qrcode.QrCodeDecoder;
|
||||||
import org.briarproject.briar.android.viewmodel.DbViewModel;
|
import org.briarproject.briar.android.viewmodel.DbViewModel;
|
||||||
@@ -23,7 +24,6 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.annotation.UiThread;
|
import androidx.annotation.UiThread;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
@@ -47,11 +47,6 @@ class MailboxViewModel extends DbViewModel
|
|||||||
|
|
||||||
private final MutableLiveData<MailboxState> state = new MutableLiveData<>();
|
private final MutableLiveData<MailboxState> state = new MutableLiveData<>();
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private String onionAddress = null;
|
|
||||||
@Nullable
|
|
||||||
private String setupToken = null;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
MailboxViewModel(
|
MailboxViewModel(
|
||||||
Application app,
|
Application app,
|
||||||
@@ -80,11 +75,6 @@ class MailboxViewModel extends DbViewModel
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
|
||||||
LiveData<MailboxState> getState() {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@IoExecutor
|
@IoExecutor
|
||||||
public void onQrCodeDecoded(Result result) {
|
public void onQrCodeDecoded(Result result) {
|
||||||
@@ -105,15 +95,25 @@ class MailboxViewModel extends DbViewModel
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] onionPubKey = Arrays.copyOfRange(bytes, 1, 33);
|
|
||||||
onionAddress = crypto.encodeOnionAddress(onionPubKey);
|
|
||||||
setupToken = StringUtils.toHexString(Arrays.copyOfRange(bytes, 33, 65))
|
|
||||||
.toLowerCase();
|
|
||||||
LOG.info("QR code is valid");
|
LOG.info("QR code is valid");
|
||||||
|
byte[] onionPubKey = Arrays.copyOfRange(bytes, 1, 33);
|
||||||
|
String onionAddress = crypto.encodeOnionAddress(onionPubKey);
|
||||||
|
byte[] tokenBytes = Arrays.copyOfRange(bytes, 33, 65);
|
||||||
|
MailboxAuthToken setupToken = new MailboxAuthToken(tokenBytes);
|
||||||
|
MailboxProperties props =
|
||||||
|
new MailboxProperties(onionAddress, setupToken, true);
|
||||||
|
// TODO pass props to core (maybe even do payload parsing there)
|
||||||
|
state.postValue(new MailboxState.SettingUp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
QrCodeDecoder getQrCodeDecoder() {
|
QrCodeDecoder getQrCodeDecoder() {
|
||||||
return qrCodeDecoder;
|
return qrCodeDecoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
LiveData<MailboxState> getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,12 +14,16 @@ 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 androidx.activity.result.ActivityResultLauncher;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
import static android.content.Intent.ACTION_SEND;
|
import static android.content.Intent.ACTION_SEND;
|
||||||
import static android.content.Intent.EXTRA_TEXT;
|
import static android.content.Intent.EXTRA_TEXT;
|
||||||
import static android.widget.Toast.LENGTH_LONG;
|
import static android.widget.Toast.LENGTH_LONG;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.showFragment;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -27,6 +31,16 @@ public class SetupDownloadFragment extends Fragment {
|
|||||||
|
|
||||||
static final String TAG = SetupDownloadFragment.class.getName();
|
static final String TAG = SetupDownloadFragment.class.getName();
|
||||||
|
|
||||||
|
private CameraPermissionManager permissionManager;
|
||||||
|
|
||||||
|
private final ActivityResultLauncher<String[]> permissionLauncher =
|
||||||
|
registerForActivityResult(new RequestMultiplePermissions(), r -> {
|
||||||
|
permissionManager.onRequestPermissionResult(r);
|
||||||
|
if (permissionManager.checkPermissions()) {
|
||||||
|
scanCode();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater,
|
public View onCreateView(LayoutInflater inflater,
|
||||||
@@ -34,10 +48,19 @@ public class SetupDownloadFragment extends Fragment {
|
|||||||
@Nullable Bundle savedInstanceState) {
|
@Nullable Bundle savedInstanceState) {
|
||||||
View v = inflater.inflate(R.layout.fragment_mailbox_setup_download,
|
View v = inflater.inflate(R.layout.fragment_mailbox_setup_download,
|
||||||
container, false);
|
container, false);
|
||||||
|
|
||||||
|
permissionManager = new CameraPermissionManager(requireActivity(),
|
||||||
|
permissionLauncher::launch);
|
||||||
|
|
||||||
Button shareLinkButton = v.findViewById(R.id.shareLinkButton);
|
Button shareLinkButton = v.findViewById(R.id.shareLinkButton);
|
||||||
Button scanButton = v.findViewById(R.id.scanButton);
|
|
||||||
shareLinkButton.setOnClickListener(this::shareLink);
|
shareLinkButton.setOnClickListener(this::shareLink);
|
||||||
scanButton.setOnClickListener(this::scanCode);
|
|
||||||
|
Button scanButton = v.findViewById(R.id.scanButton);
|
||||||
|
scanButton.setOnClickListener(view -> {
|
||||||
|
if (permissionManager.checkPermissions()) {
|
||||||
|
scanCode();
|
||||||
|
}
|
||||||
|
});
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,6 +68,8 @@ public class SetupDownloadFragment extends Fragment {
|
|||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
requireActivity().setTitle(R.string.mailbox_setup_title);
|
requireActivity().setTitle(R.string.mailbox_setup_title);
|
||||||
|
// Permissions may have been granted manually while we were stopped
|
||||||
|
permissionManager.resetPermissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void shareLink(View v) {
|
private void shareLink(View v) {
|
||||||
@@ -69,8 +94,10 @@ public class SetupDownloadFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scanCode(View v) {
|
private void scanCode() {
|
||||||
Toast.makeText(requireContext(), "TODO", LENGTH_LONG).show();
|
FragmentManager fm = getParentFragmentManager();
|
||||||
|
Fragment f = new MailboxScanFragment();
|
||||||
|
showFragment(fm, f, MailboxScanFragment.TAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package org.briarproject.briar.android.util;
|
||||||
|
|
||||||
|
public enum Permission {
|
||||||
|
UNKNOWN, GRANTED, SHOW_RATIONALE, PERMANENTLY_DENIED
|
||||||
|
}
|
||||||
@@ -52,6 +52,7 @@ import androidx.annotation.ColorRes;
|
|||||||
import androidx.annotation.DrawableRes;
|
import androidx.annotation.DrawableRes;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
import androidx.annotation.UiThread;
|
import androidx.annotation.UiThread;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
@@ -574,4 +575,26 @@ public class UiUtils {
|
|||||||
SOFT_INPUT_STATE_HIDDEN);
|
SOFT_INPUT_STATE_HIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void showDenialDialog(FragmentActivity ctx,
|
||||||
|
@StringRes int title, @StringRes int body) {
|
||||||
|
AlertDialog.Builder builder =
|
||||||
|
new AlertDialog.Builder(ctx, R.style.BriarDialogTheme);
|
||||||
|
builder.setTitle(title);
|
||||||
|
builder.setMessage(body);
|
||||||
|
builder.setPositiveButton(R.string.ok, getGoToSettingsListener(ctx));
|
||||||
|
builder.setNegativeButton(R.string.cancel,
|
||||||
|
(dialog, which) -> ctx.supportFinishAfterTransition());
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void showRationale(FragmentActivity ctx, @StringRes int title,
|
||||||
|
@StringRes int body, Runnable requestPermissions) {
|
||||||
|
AlertDialog.Builder builder =
|
||||||
|
new AlertDialog.Builder(ctx, R.style.BriarDialogTheme);
|
||||||
|
builder.setTitle(title);
|
||||||
|
builder.setMessage(body);
|
||||||
|
builder.setNeutralButton(R.string.continue_button,
|
||||||
|
(dialog, which) -> requestPermissions.run());
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
briar-android/src/main/res/drawable/border_qr_scanner.xml
Normal file
14
briar-android/src/main/res/drawable/border_qr_scanner.xml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:dither="true"
|
||||||
|
android:shape="rectangle">
|
||||||
|
|
||||||
|
<corners android:radius="32dp" />
|
||||||
|
|
||||||
|
<solid android:color="@android:color/transparent" />
|
||||||
|
|
||||||
|
<stroke
|
||||||
|
android:width="4dp"
|
||||||
|
android:color="#ffffff" />
|
||||||
|
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progressBar"
|
||||||
|
style="?android:attr/progressBarStyleLarge"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/textView"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView"
|
||||||
|
style="@style/TextAppearance.AppCompat.Large"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:text="@string/mailbox_setup_connecting"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/progressBar" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
27
briar-android/src/main/res/layout/fragment_mailbox_scan.xml
Normal file
27
briar-android/src/main/res/layout/fragment_mailbox_scan.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<org.briarproject.briar.android.qrcode.CameraView
|
||||||
|
android:id="@+id/camera_view"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:background="@drawable/border_qr_scanner"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintDimensionRatio="1,1"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -628,6 +628,8 @@
|
|||||||
<string name="mailbox_share_fdroid" translatable="false">https://f-droid.org/packages/org.briarproject.mailbox/</string>
|
<string name="mailbox_share_fdroid" translatable="false">https://f-droid.org/packages/org.briarproject.mailbox/</string>
|
||||||
<string name="mailbox_share_gplay" translatable="false">https://play.google.com/store/apps/details?id=org.briarproject.mailbox</string>
|
<string name="mailbox_share_gplay" translatable="false">https://play.google.com/store/apps/details?id=org.briarproject.mailbox</string>
|
||||||
<string name="mailbox_share_download" translatable="false">https://briarproject.org/apk</string>
|
<string name="mailbox_share_download" translatable="false">https://briarproject.org/apk</string>
|
||||||
|
<string name="permission_camera_qr_denied_body">You have denied access to the camera, but scanning a QR code requires using the camera.\n\nPlease consider granting access.</string>
|
||||||
|
<string name="mailbox_setup_connecting">Connecting…</string>
|
||||||
|
|
||||||
<!-- Conversation Settings -->
|
<!-- Conversation Settings -->
|
||||||
<string name="disappearing_messages_title">Disappearing messages</string>
|
<string name="disappearing_messages_title">Disappearing messages</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user