diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java index 42d6570e2..82f7d5aae 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java @@ -44,6 +44,7 @@ import org.briarproject.briar.android.hotspot.ManualHotspotFragment; import org.briarproject.briar.android.hotspot.QrHotspotFragment; import org.briarproject.briar.android.logging.CachingLogHandler; 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.ReceiveFragment; import org.briarproject.briar.android.removabledrive.SendFragment; @@ -239,4 +240,6 @@ public interface AndroidComponent void inject(ReceiveFragment receiveFragment); void inject(BluetoothIntroFragment bluetoothIntroFragment); + + void inject(MailboxScanFragment mailboxScanFragment); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/AddNearbyContactPermissionManager.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/AddNearbyContactPermissionManager.java index dc8fca71f..f40dbe094 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/AddNearbyContactPermissionManager.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/AddNearbyContactPermissionManager.java @@ -3,11 +3,10 @@ package org.briarproject.briar.android.contact.add.nearby; import android.content.Context; import org.briarproject.briar.R; +import org.briarproject.briar.android.util.Permission; import java.util.Map; -import androidx.annotation.StringRes; -import androidx.appcompat.app.AlertDialog; import androidx.core.util.Consumer; 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 androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale; 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.showDenialDialog; import static org.briarproject.briar.android.util.UiUtils.showLocationDialog; +import static org.briarproject.briar.android.util.UiUtils.showRationale; class AddNearbyContactPermissionManager { - private enum Permission { - UNKNOWN, GRANTED, SHOW_RATIONALE, PERMANENTLY_DENIED - } - private Permission cameraPermission = Permission.UNKNOWN; private Permission locationPermission = Permission.UNKNOWN; @@ -68,27 +64,30 @@ class AddNearbyContactPermissionManager { // If an essential permission has been permanently denied, ask the // user to change the setting if (cameraPermission == Permission.PERMANENTLY_DENIED) { - showDenialDialog(R.string.permission_camera_title, + showDenialDialog(ctx, R.string.permission_camera_title, R.string.permission_camera_denied_body); return false; } if (isBluetoothSupported && locationPermission == Permission.PERMANENTLY_DENIED) { - showDenialDialog(R.string.permission_location_title, + showDenialDialog(ctx, R.string.permission_location_title, R.string.permission_location_denied_body); return false; } // Should we show the rationale for one or both permissions? if (cameraPermission == Permission.SHOW_RATIONALE && locationPermission == Permission.SHOW_RATIONALE) { - showRationale(R.string.permission_camera_location_title, - R.string.permission_camera_location_request_body); + showRationale(ctx, R.string.permission_camera_location_title, + R.string.permission_camera_location_request_body, + this::requestPermissions); } else if (cameraPermission == Permission.SHOW_RATIONALE) { - showRationale(R.string.permission_camera_title, - R.string.permission_camera_request_body); + showRationale(ctx, R.string.permission_camera_title, + R.string.permission_camera_request_body, + this::requestPermissions); } else if (locationPermission == Permission.SHOW_RATIONALE) { - showRationale(R.string.permission_location_title, - R.string.permission_location_request_body); + showRationale(ctx, R.string.permission_location_title, + R.string.permission_location_request_body, + this::requestPermissions); } else if (locationEnabled) { requestPermissions(); } else { @@ -97,27 +96,6 @@ class AddNearbyContactPermissionManager { 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() { String[] permissions; if (isBluetoothSupported) { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/connect/BluetoothConditionManager.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/connect/BluetoothConditionManager.java index a2b2a4cf7..7a8ac1d9a 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/connect/BluetoothConditionManager.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/connect/BluetoothConditionManager.java @@ -4,6 +4,7 @@ import android.app.Activity; import android.content.Context; import org.briarproject.briar.R; +import org.briarproject.briar.android.util.Permission; import androidx.activity.result.ActivityResultLauncher; import androidx.annotation.Nullable; @@ -19,10 +20,6 @@ import static org.briarproject.briar.android.util.UiUtils.showLocationDialog; class BluetoothConditionManager { - private enum Permission { - UNKNOWN, GRANTED, SHOW_RATIONALE, PERMANENTLY_DENIED - } - private Permission locationPermission = Permission.UNKNOWN; /** diff --git a/briar-android/src/main/java/org/briarproject/briar/android/hotspot/AbstractConditionManager.java b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/AbstractConditionManager.java index 855fd845d..d0e65a93f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/hotspot/AbstractConditionManager.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/AbstractConditionManager.java @@ -20,11 +20,7 @@ import static android.content.Context.WIFI_SERVICE; */ abstract class AbstractConditionManager { - enum Permission { - UNKNOWN, GRANTED, SHOW_RATIONALE, PERMANENTLY_DENIED - } - - protected final Consumer permissionUpdateCallback; + final Consumer permissionUpdateCallback; protected FragmentActivity ctx; WifiManager wifiManager; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/hotspot/ConditionManager29.java b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/ConditionManager29.java index 036225899..071fbeeb6 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/hotspot/ConditionManager29.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/ConditionManager29.java @@ -4,6 +4,7 @@ import android.content.Intent; import android.provider.Settings; import org.briarproject.briar.R; +import org.briarproject.briar.android.util.Permission; import java.util.logging.Logger; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/mailbox/CameraPermissionManager.java b/briar-android/src/main/java/org/briarproject/briar/android/mailbox/CameraPermissionManager.java new file mode 100644 index 000000000..34e819185 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/mailbox/CameraPermissionManager.java @@ -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 requestPermissions; + + CameraPermissionManager(FragmentActivity ctx, + Consumer 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 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 result) { + Boolean permissionResult = result.get(CAMERA); + return permissionResult == null ? areEssentialPermissionsGranted(ctx) : + permissionResult; + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/mailbox/MailboxActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/mailbox/MailboxActivity.java index 739bcae3b..e8056dad2 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/mailbox/MailboxActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/mailbox/MailboxActivity.java @@ -17,6 +17,7 @@ import androidx.lifecycle.ViewModelProvider; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; +import static org.briarproject.briar.android.util.UiUtils.showFragment; @MethodsNotNullByDefault @ParametersNotNullByDefault @@ -46,13 +47,13 @@ public class MailboxActivity extends BriarActivity { progressBar.setVisibility(VISIBLE); } - if (savedInstanceState == null) { - viewModel.getState().observe(this, state -> { - if (state instanceof MailboxState.NotSetup) { - onNotSetup(); - } - }); - } + viewModel.getState().observe(this, state -> { + if (state instanceof MailboxState.NotSetup) { + if (savedInstanceState == null) onNotSetup(); + } else if (state instanceof MailboxState.SettingUp) { + onCodeScanned(); + } + }); } @Override @@ -64,6 +65,17 @@ public class MailboxActivity extends BriarActivity { 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() { progressBar.setVisibility(INVISIBLE); getSupportFragmentManager().beginTransaction() @@ -72,4 +84,10 @@ public class MailboxActivity extends BriarActivity { .commit(); } + private void onCodeScanned() { + showFragment(getSupportFragmentManager(), + new MailboxConnectingFragment(), + MailboxConnectingFragment.TAG, false); + } + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/mailbox/MailboxConnectingFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/mailbox/MailboxConnectingFragment.java new file mode 100644 index 000000000..3361c01cd --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/mailbox/MailboxConnectingFragment.java @@ -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); + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/mailbox/MailboxScanFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/mailbox/MailboxScanFragment.java new file mode 100644 index 000000000..335fd90f8 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/mailbox/MailboxScanFragment.java @@ -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(); + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/mailbox/MailboxState.java b/briar-android/src/main/java/org/briarproject/briar/android/mailbox/MailboxState.java index 13f2be94b..2b9f32596 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/mailbox/MailboxState.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/mailbox/MailboxState.java @@ -5,6 +5,9 @@ class MailboxState { static class NotSetup extends MailboxState { } + static class SettingUp extends MailboxState { + } + // TODO add other states } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/mailbox/MailboxViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/mailbox/MailboxViewModel.java index 9750b8e87..7a009ad69 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/mailbox/MailboxViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/mailbox/MailboxViewModel.java @@ -9,9 +9,10 @@ import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.TransactionManager; import org.briarproject.bramble.api.lifecycle.IoExecutor; 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.system.AndroidExecutor; -import org.briarproject.bramble.util.StringUtils; import org.briarproject.briar.android.mailbox.MailboxState.NotSetup; import org.briarproject.briar.android.qrcode.QrCodeDecoder; import org.briarproject.briar.android.viewmodel.DbViewModel; @@ -23,7 +24,6 @@ import java.util.logging.Logger; import javax.inject.Inject; -import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; @@ -47,11 +47,6 @@ class MailboxViewModel extends DbViewModel private final MutableLiveData state = new MutableLiveData<>(); - @Nullable - private String onionAddress = null; - @Nullable - private String setupToken = null; - @Inject MailboxViewModel( Application app, @@ -80,11 +75,6 @@ class MailboxViewModel extends DbViewModel }); } - @UiThread - LiveData getState() { - return state; - } - @Override @IoExecutor public void onQrCodeDecoded(Result result) { @@ -105,15 +95,25 @@ class MailboxViewModel extends DbViewModel 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"); + 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() { return qrCodeDecoder; } + @UiThread + LiveData getState() { + return state; + } + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/mailbox/SetupDownloadFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/mailbox/SetupDownloadFragment.java index 9ecd01e51..b1683b885 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/mailbox/SetupDownloadFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/mailbox/SetupDownloadFragment.java @@ -14,12 +14,16 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.briar.R; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; import static android.content.Intent.ACTION_SEND; import static android.content.Intent.EXTRA_TEXT; import static android.widget.Toast.LENGTH_LONG; +import static org.briarproject.briar.android.util.UiUtils.showFragment; @MethodsNotNullByDefault @ParametersNotNullByDefault @@ -27,6 +31,16 @@ public class SetupDownloadFragment extends Fragment { static final String TAG = SetupDownloadFragment.class.getName(); + private CameraPermissionManager permissionManager; + + private final ActivityResultLauncher permissionLauncher = + registerForActivityResult(new RequestMultiplePermissions(), r -> { + permissionManager.onRequestPermissionResult(r); + if (permissionManager.checkPermissions()) { + scanCode(); + } + }); + @Nullable @Override public View onCreateView(LayoutInflater inflater, @@ -34,10 +48,19 @@ public class SetupDownloadFragment extends Fragment { @Nullable Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_mailbox_setup_download, container, false); + + permissionManager = new CameraPermissionManager(requireActivity(), + permissionLauncher::launch); + Button shareLinkButton = v.findViewById(R.id.shareLinkButton); - Button scanButton = v.findViewById(R.id.scanButton); shareLinkButton.setOnClickListener(this::shareLink); - scanButton.setOnClickListener(this::scanCode); + + Button scanButton = v.findViewById(R.id.scanButton); + scanButton.setOnClickListener(view -> { + if (permissionManager.checkPermissions()) { + scanCode(); + } + }); return v; } @@ -45,6 +68,8 @@ public class SetupDownloadFragment extends Fragment { public void onStart() { super.onStart(); requireActivity().setTitle(R.string.mailbox_setup_title); + // Permissions may have been granted manually while we were stopped + permissionManager.resetPermissions(); } private void shareLink(View v) { @@ -69,8 +94,10 @@ public class SetupDownloadFragment extends Fragment { } } - private void scanCode(View v) { - Toast.makeText(requireContext(), "TODO", LENGTH_LONG).show(); + private void scanCode() { + FragmentManager fm = getParentFragmentManager(); + Fragment f = new MailboxScanFragment(); + showFragment(fm, f, MailboxScanFragment.TAG); } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/Permission.java b/briar-android/src/main/java/org/briarproject/briar/android/util/Permission.java new file mode 100644 index 000000000..5ebd1f224 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/util/Permission.java @@ -0,0 +1,5 @@ +package org.briarproject.briar.android.util; + +public enum Permission { + UNKNOWN, GRANTED, SHOW_RATIONALE, PERMANENTLY_DENIED +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java index 9c6f182b2..3c43564ae 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java @@ -52,6 +52,7 @@ import androidx.annotation.ColorRes; import androidx.annotation.DrawableRes; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; +import androidx.annotation.StringRes; import androidx.annotation.UiThread; import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; @@ -574,4 +575,26 @@ public class UiUtils { 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(); + } } diff --git a/briar-android/src/main/res/drawable/border_qr_scanner.xml b/briar-android/src/main/res/drawable/border_qr_scanner.xml new file mode 100644 index 000000000..c059d1504 --- /dev/null +++ b/briar-android/src/main/res/drawable/border_qr_scanner.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/briar-android/src/main/res/layout/fragment_mailbox_connecting.xml b/briar-android/src/main/res/layout/fragment_mailbox_connecting.xml new file mode 100644 index 000000000..c7cf2b242 --- /dev/null +++ b/briar-android/src/main/res/layout/fragment_mailbox_connecting.xml @@ -0,0 +1,31 @@ + + + + + + + + diff --git a/briar-android/src/main/res/layout/fragment_mailbox_scan.xml b/briar-android/src/main/res/layout/fragment_mailbox_scan.xml new file mode 100644 index 000000000..b93440c7a --- /dev/null +++ b/briar-android/src/main/res/layout/fragment_mailbox_scan.xml @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index dfd702879..6010f7a55 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -628,6 +628,8 @@ https://f-droid.org/packages/org.briarproject.mailbox/ https://play.google.com/store/apps/details?id=org.briarproject.mailbox https://briarproject.org/apk + You have denied access to the camera, but scanning a QR code requires using the camera.\n\nPlease consider granting access. + Connecting… Disappearing messages