Address review feedback

This commit is contained in:
Torsten Grote
2022-09-20 15:19:05 -03:00
parent 4a65bc1726
commit 0a906998fe
15 changed files with 263 additions and 241 deletions

View File

@@ -23,7 +23,6 @@ import java.util.Scanner;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_SCAN;
import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.content.Context.MODE_PRIVATE; import static android.content.Context.MODE_PRIVATE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -54,11 +53,6 @@ public class AndroidUtils {
return abis; return abis;
} }
public static boolean hasBtScanPermission(Context ctx) {
return SDK_INT < 31 || ctx.checkPermission(BLUETOOTH_SCAN, myPid(),
myUid()) == PERMISSION_GRANTED;
}
public static boolean hasBtConnectPermission(Context ctx) { public static boolean hasBtConnectPermission(Context ctx) {
return SDK_INT < 31 || ctx.checkPermission(BLUETOOTH_CONNECT, myPid(), return SDK_INT < 31 || ctx.checkPermission(BLUETOOTH_CONNECT, myPid(),
myUid()) == PERMISSION_GRANTED; myUid()) == PERMISSION_GRANTED;

View File

@@ -91,9 +91,10 @@ abstract class AbstractBluetoothPlugin<S, SS> implements BluetoothPlugin,
/** /**
* Override and return true, if the plugin is now allowed to access the * Override and return true, if the plugin is now allowed to access the
* Bluetooth hardware, so it must be * Bluetooth hardware.
* If this returns false, the plugin must be
* {@link org.briarproject.bramble.api.plugin.Plugin.State#DISABLED} * {@link org.briarproject.bramble.api.plugin.Plugin.State#DISABLED}
* in {@link #start()}. * in {@link #start()} and not attempt to access Bluetooth hardware.
*/ */
protected boolean isBluetoothAccessible() { protected boolean isBluetoothAccessible() {
return true; return true;
@@ -553,7 +554,8 @@ abstract class AbstractBluetoothPlugin<S, SS> implements BluetoothPlugin,
private void onSettingsUpdated(Settings settings) { private void onSettingsUpdated(Settings settings) {
boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE, boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE,
DEFAULT_PREF_PLUGIN_ENABLE); DEFAULT_PREF_PLUGIN_ENABLE);
SS ss = state.setEnabledByUser(enabledByUser); boolean shouldEnable = enabledByUser && isBluetoothAccessible();
SS ss = state.setEnabledByUser(shouldEnable);
State s = getState(); State s = getState();
if (ss != null) { if (ss != null) {
LOG.info("Disabled by user, closing server socket"); LOG.info("Disabled by user, closing server socket");

View File

@@ -19,17 +19,17 @@ 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.bramble.util.AndroidUtils.hasBtConnectPermission;
import static org.briarproject.bramble.util.AndroidUtils.hasBtScanPermission;
import static org.briarproject.briar.android.util.Permission.GRANTED; import static org.briarproject.briar.android.util.Permission.GRANTED;
import static org.briarproject.briar.android.util.Permission.PERMANENTLY_DENIED; import static org.briarproject.briar.android.util.Permission.PERMANENTLY_DENIED;
import static org.briarproject.briar.android.util.Permission.SHOW_RATIONALE; import static org.briarproject.briar.android.util.Permission.SHOW_RATIONALE;
import static org.briarproject.briar.android.util.Permission.UNKNOWN; import static org.briarproject.briar.android.util.Permission.UNKNOWN;
import static org.briarproject.briar.android.util.UiUtils.isLocationEnabled; import static org.briarproject.briar.android.util.PermissionUtils.areBluetoothPermissionsGranted;
import static org.briarproject.briar.android.util.UiUtils.showDenialDialog; import static org.briarproject.briar.android.util.PermissionUtils.gotPermission;
import static org.briarproject.briar.android.util.UiUtils.showLocationDialog; import static org.briarproject.briar.android.util.PermissionUtils.isLocationEnabledForBt;
import static org.briarproject.briar.android.util.UiUtils.showRationale; import static org.briarproject.briar.android.util.PermissionUtils.showDenialDialog;
import static org.briarproject.briar.android.util.UiUtils.wasGrantedBluetoothPermissions; import static org.briarproject.briar.android.util.PermissionUtils.showLocationDialog;
import static org.briarproject.briar.android.util.PermissionUtils.showRationale;
import static org.briarproject.briar.android.util.PermissionUtils.wasGrantedBluetoothPermissions;
class AddNearbyContactPermissionManager { class AddNearbyContactPermissionManager {
@@ -64,9 +64,7 @@ class AddNearbyContactPermissionManager {
} else if (SDK_INT < 31) { } else if (SDK_INT < 31) {
bluetoothOk = checkSelfPermission(ctx, ACCESS_FINE_LOCATION) == ok; bluetoothOk = checkSelfPermission(ctx, ACCESS_FINE_LOCATION) == ok;
} else { } else {
bluetoothOk = hasBtConnectPermission(ctx) && bluetoothOk = areBluetoothPermissionsGranted(ctx);
hasBtScanPermission(ctx) &&
checkSelfPermission(ctx, BLUETOOTH_ADVERTISE) == ok;
} }
return bluetoothOk && checkSelfPermission(ctx, CAMERA) == ok; return bluetoothOk && checkSelfPermission(ctx, CAMERA) == ok;
} }
@@ -79,7 +77,7 @@ class AddNearbyContactPermissionManager {
} }
boolean checkPermissions() { boolean checkPermissions() {
boolean locationEnabled = isLocationEnabled(ctx); boolean locationEnabled = isLocationEnabledForBt(ctx);
if (locationEnabled && areEssentialPermissionsGranted()) return true; if (locationEnabled && areEssentialPermissionsGranted()) return true;
// 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
@@ -141,7 +139,7 @@ class AddNearbyContactPermissionManager {
} }
void onRequestPermissionResult(Map<String, Boolean> result) { void onRequestPermissionResult(Map<String, Boolean> result) {
if (gotPermission(CAMERA, result)) { if (gotPermission(ctx, result, CAMERA)) {
cameraPermission = GRANTED; cameraPermission = GRANTED;
} else if (shouldShowRationale(CAMERA)) { } else if (shouldShowRationale(CAMERA)) {
cameraPermission = SHOW_RATIONALE; cameraPermission = SHOW_RATIONALE;
@@ -150,7 +148,7 @@ class AddNearbyContactPermissionManager {
} }
if (isBluetoothSupported) { if (isBluetoothSupported) {
if (SDK_INT < 31) { if (SDK_INT < 31) {
if (gotPermission(ACCESS_FINE_LOCATION, result)) { if (gotPermission(ctx, result, ACCESS_FINE_LOCATION)) {
locationPermission = GRANTED; locationPermission = GRANTED;
} else if (shouldShowRationale(ACCESS_FINE_LOCATION)) { } else if (shouldShowRationale(ACCESS_FINE_LOCATION)) {
locationPermission = SHOW_RATIONALE; locationPermission = SHOW_RATIONALE;
@@ -158,7 +156,7 @@ class AddNearbyContactPermissionManager {
locationPermission = PERMANENTLY_DENIED; locationPermission = PERMANENTLY_DENIED;
} }
} else { } else {
if (wasGrantedBluetoothPermissions(result)) { if (wasGrantedBluetoothPermissions(ctx, result)) {
bluetoothPermissions = GRANTED; bluetoothPermissions = GRANTED;
} else if (shouldShowRationale(BLUETOOTH_CONNECT)) { } else if (shouldShowRationale(BLUETOOTH_CONNECT)) {
bluetoothPermissions = SHOW_RATIONALE; bluetoothPermissions = SHOW_RATIONALE;
@@ -169,17 +167,6 @@ class AddNearbyContactPermissionManager {
} }
} }
private boolean gotPermission(String permission,
Map<String, Boolean> result) {
Boolean permissionResult = result.get(permission);
return permissionResult == null ?
isGranted(permission) : permissionResult;
}
private boolean isGranted(String permission) {
return checkSelfPermission(ctx, permission) == PERMISSION_GRANTED;
}
private boolean shouldShowRationale(String permission) { private boolean shouldShowRationale(String permission) {
return shouldShowRequestPermissionRationale(ctx, permission); return shouldShowRequestPermissionRationale(ctx, permission);
} }

View File

@@ -89,8 +89,8 @@ import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContact
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.NO_ADAPTER; import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.NO_ADAPTER;
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.REFUSED; import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.REFUSED;
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.UNKNOWN; import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.UNKNOWN;
import static org.briarproject.briar.android.util.PermissionUtils.isLocationEnabledForBt;
import static org.briarproject.briar.android.util.UiUtils.handleException; import static org.briarproject.briar.android.util.UiUtils.handleException;
import static org.briarproject.briar.android.util.UiUtils.isLocationEnabled;
@NotNullByDefault @NotNullByDefault
class AddNearbyContactViewModel extends AndroidViewModel class AddNearbyContactViewModel extends AndroidViewModel
@@ -396,7 +396,7 @@ class AddNearbyContactViewModel extends AndroidViewModel
void showQrCodeFragmentIfAllowed() { void showQrCodeFragmentIfAllowed() {
boolean permissionsGranted = areEssentialPermissionsGranted( boolean permissionsGranted = areEssentialPermissionsGranted(
getApplication(), isBluetoothSupported()); getApplication(), isBluetoothSupported());
boolean locationEnabled = isLocationEnabled(getApplication()); boolean locationEnabled = isLocationEnabledForBt(getApplication());
if (wasContinueClicked && permissionsGranted && locationEnabled) { if (wasContinueClicked && permissionsGranted && locationEnabled) {
if (isWifiReady() && isBluetoothReady()) { if (isWifiReady() && isBluetoothReady()) {
LOG.info("Wifi and Bluetooth are ready"); LOG.info("Wifi and Bluetooth are ready");

View File

@@ -1,35 +1,34 @@
package org.briarproject.briar.android.contact.connect; package org.briarproject.briar.android.contact.connect;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.widget.Toast;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.util.Permission; import org.briarproject.briar.android.util.Permission;
import org.briarproject.briar.android.util.UiUtils;
import java.util.Map; import java.util.Map;
import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import static android.Manifest.permission.ACCESS_FINE_LOCATION; import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.Manifest.permission.BLUETOOTH_CONNECT;
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 android.widget.Toast.LENGTH_LONG;
import static androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale; import static androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale;
import static androidx.core.content.ContextCompat.checkSelfPermission;
import static org.briarproject.briar.android.util.Permission.GRANTED; import static org.briarproject.briar.android.util.Permission.GRANTED;
import static org.briarproject.briar.android.util.Permission.PERMANENTLY_DENIED; import static org.briarproject.briar.android.util.Permission.PERMANENTLY_DENIED;
import static org.briarproject.briar.android.util.Permission.SHOW_RATIONALE; import static org.briarproject.briar.android.util.Permission.SHOW_RATIONALE;
import static org.briarproject.briar.android.util.Permission.UNKNOWN; import static org.briarproject.briar.android.util.Permission.UNKNOWN;
import static org.briarproject.briar.android.util.UiUtils.getGoToSettingsListener; import static org.briarproject.briar.android.util.PermissionUtils.gotPermission;
import static org.briarproject.briar.android.util.UiUtils.isLocationEnabled; import static org.briarproject.briar.android.util.PermissionUtils.isLocationEnabledForBt;
import static org.briarproject.briar.android.util.UiUtils.requestBluetoothPermissions; import static org.briarproject.briar.android.util.PermissionUtils.requestBluetoothPermissions;
import static org.briarproject.briar.android.util.UiUtils.showLocationDialog; import static org.briarproject.briar.android.util.PermissionUtils.showDenialDialog;
import static org.briarproject.briar.android.util.UiUtils.wasGrantedBluetoothPermissions; import static org.briarproject.briar.android.util.PermissionUtils.showLocationDialog;
import static org.briarproject.briar.android.util.PermissionUtils.showRationale;
import static org.briarproject.briar.android.util.PermissionUtils.wasGrantedBluetoothPermissions;
class BluetoothConditionManager { class BluetoothConditionManager {
@@ -48,7 +47,7 @@ class BluetoothConditionManager {
@UiThread @UiThread
void requestPermissions(ActivityResultLauncher<String[]> launcher) { void requestPermissions(ActivityResultLauncher<String[]> launcher) {
if (SDK_INT < 31) { if (SDK_INT < 31) {
launcher.launch(new String[] {ACCESS_FINE_LOCATION}); requestLocationPermission(launcher);
} else { } else {
requestBluetoothPermissions(launcher); requestBluetoothPermissions(launcher);
} }
@@ -58,7 +57,7 @@ class BluetoothConditionManager {
void onLocationPermissionResult(Activity activity, void onLocationPermissionResult(Activity activity,
@Nullable Map<String, Boolean> result) { @Nullable Map<String, Boolean> result) {
if (SDK_INT < 31) { if (SDK_INT < 31) {
if (gotPermission(activity, result)) { if (gotPermission(activity, result, ACCESS_FINE_LOCATION)) {
locationPermission = GRANTED; locationPermission = GRANTED;
} else if (shouldShowRequestPermissionRationale(activity, } else if (shouldShowRequestPermissionRationale(activity,
ACCESS_FINE_LOCATION)) { ACCESS_FINE_LOCATION)) {
@@ -67,7 +66,7 @@ class BluetoothConditionManager {
locationPermission = PERMANENTLY_DENIED; locationPermission = PERMANENTLY_DENIED;
} }
} else { } else {
if (wasGrantedBluetoothPermissions(result)) { if (wasGrantedBluetoothPermissions(activity, result)) {
bluetoothPermissions = GRANTED; bluetoothPermissions = GRANTED;
} else if (shouldShowRequestPermissionRationale(activity, } else if (shouldShowRequestPermissionRationale(activity,
BLUETOOTH_CONNECT)) { BLUETOOTH_CONNECT)) {
@@ -84,58 +83,38 @@ class BluetoothConditionManager {
boolean permissionGranted = boolean permissionGranted =
(SDK_INT < 23 || locationPermission == GRANTED) && (SDK_INT < 23 || locationPermission == GRANTED) &&
bluetoothPermissions == GRANTED; bluetoothPermissions == GRANTED;
boolean locationEnabled = isLocationEnabled(ctx); boolean locationEnabled = isLocationEnabledForBt(ctx);
if (permissionGranted && locationEnabled) return true; if (permissionGranted && locationEnabled) return true;
if (locationPermission == PERMANENTLY_DENIED) { if (locationPermission == PERMANENTLY_DENIED) {
showDenialDialog(ctx, onLocationDenied); showDenialDialog(ctx, R.string.permission_location_title,
R.string.permission_location_denied_body, onLocationDenied);
} else if (locationPermission == SHOW_RATIONALE) { } else if (locationPermission == SHOW_RATIONALE) {
showRationale(ctx, permissionRequest); showRationale(ctx, R.string.permission_location_title,
R.string.permission_location_request_body,
() -> requestLocationPermission(permissionRequest));
} else if (!locationEnabled) { } else if (!locationEnabled) {
showLocationDialog(ctx); showLocationDialog(ctx);
} else if (bluetoothPermissions == PERMANENTLY_DENIED) { } else if (bluetoothPermissions == PERMANENTLY_DENIED) {
UiUtils.showDenialDialog(ctx, R.string.permission_bluetooth_title, Runnable onDenied = () -> Toast.makeText(ctx,
R.string.permission_bluetooth_denied_body); R.string.connect_via_bluetooth_no_bluetooth_permission,
LENGTH_LONG).show();
showDenialDialog(ctx, R.string.permission_bluetooth_title,
R.string.permission_bluetooth_denied_body, onDenied);
} else if (bluetoothPermissions == SHOW_RATIONALE && SDK_INT >= 31) { } else if (bluetoothPermissions == SHOW_RATIONALE && SDK_INT >= 31) {
UiUtils.showRationale(ctx, R.string.permission_bluetooth_title, // SDK_INT is checked to make linter happy, because
// requestBluetoothPermissions() requires SDK_INT 31
showRationale(ctx,
R.string.permission_bluetooth_title,
R.string.permission_bluetooth_body, () -> R.string.permission_bluetooth_body, () ->
requestBluetoothPermissions(permissionRequest)); requestBluetoothPermissions(permissionRequest));
} }
return false; return false;
} }
private void showDenialDialog(Context ctx, Runnable onLocationDenied) { private void requestLocationPermission(
new AlertDialog.Builder(ctx, R.style.BriarDialogTheme) ActivityResultLauncher<String[]> launcher) {
.setTitle(R.string.permission_location_title) launcher.launch(new String[] {ACCESS_FINE_LOCATION});
.setMessage(R.string.permission_location_denied_body)
.setPositiveButton(R.string.ok, getGoToSettingsListener(ctx))
.setNegativeButton(R.string.cancel, (v, d) ->
onLocationDenied.run())
.show();
}
private void showRationale(Context ctx,
ActivityResultLauncher<String[]> permissionRequest) {
new AlertDialog.Builder(ctx, R.style.BriarDialogTheme)
.setTitle(R.string.permission_location_title)
.setMessage(R.string.permission_location_request_body)
.setPositiveButton(R.string.ok, (dialog, which) ->
permissionRequest.launch(
new String[] {ACCESS_FINE_LOCATION}))
.show();
}
private boolean gotPermission(Context ctx,
@Nullable Map<String, Boolean> result) {
Boolean permissionResult =
result == null ? null : result.get(ACCESS_FINE_LOCATION);
return permissionResult == null ? isLocationPermissionGranted(ctx) :
permissionResult;
}
private boolean isLocationPermissionGranted(Context ctx) {
return checkSelfPermission(ctx, ACCESS_FINE_LOCATION) ==
PERMISSION_GRANTED;
} }
} }

View File

@@ -46,6 +46,7 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
import static org.briarproject.briar.android.util.PermissionUtils.areBluetoothPermissionsGranted;
@UiThread @UiThread
@NotNullByDefault @NotNullByDefault
@@ -260,7 +261,8 @@ class ConnectViaBluetoothViewModel extends DbViewModel implements
private void stopConnecting() { private void stopConnecting() {
BluetoothPlugin bluetoothPlugin = this.bluetoothPlugin; BluetoothPlugin bluetoothPlugin = this.bluetoothPlugin;
if (bluetoothPlugin != null) { if (bluetoothPlugin != null &&
areBluetoothPermissionsGranted(getApplication())) {
bluetoothPlugin.stopDiscoverAndConnect(); bluetoothPlugin.stopDiscoverAndConnect();
} }
} }

View File

@@ -1,7 +1,6 @@
package org.briarproject.briar.android.hotspot; package org.briarproject.briar.android.hotspot;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.net.wifi.WifiManager; import android.net.wifi.WifiManager;
import org.briarproject.briar.R; import org.briarproject.briar.R;
@@ -20,6 +19,12 @@ import static android.content.Context.WIFI_SERVICE;
*/ */
abstract class AbstractConditionManager { abstract class AbstractConditionManager {
/**
* Consumes false, if permissions have been denied. Then we don't call
* {@link HotspotIntroFragment#startHotspotIfConditionsFulfilled()},
* which would result in the same permission being requested again
* immediately.
*/
final Consumer<Boolean> permissionUpdateCallback; final Consumer<Boolean> permissionUpdateCallback;
protected FragmentActivity ctx; protected FragmentActivity ctx;
WifiManager wifiManager; WifiManager wifiManager;
@@ -52,19 +57,6 @@ abstract class AbstractConditionManager {
*/ */
abstract boolean checkAndRequestConditions(); abstract boolean checkAndRequestConditions();
void showDenialDialog(FragmentActivity ctx,
@StringRes int title, @StringRes int body,
DialogInterface.OnClickListener onOkClicked, Runnable onDismiss) {
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
builder.setTitle(title);
builder.setMessage(body);
builder.setPositiveButton(R.string.ok, onOkClicked);
builder.setNegativeButton(R.string.cancel,
(dialog, which) -> ctx.supportFinishAfterTransition());
builder.setOnDismissListener(dialog -> onDismiss.run());
builder.show();
}
void showRationale(Context ctx, @StringRes int title, void showRationale(Context ctx, @StringRes int title,
@StringRes int body, Runnable onContinueClicked, @StringRes int body, Runnable onContinueClicked,
Runnable onDismiss) { Runnable onDismiss) {

View File

@@ -1,11 +1,11 @@
package org.briarproject.briar.android.hotspot; package org.briarproject.briar.android.hotspot;
import android.content.Intent; import android.content.Intent;
import android.location.LocationManager;
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 org.briarproject.briar.android.util.Permission;
import org.briarproject.briar.android.util.PermissionUtils;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -23,8 +23,8 @@ import static androidx.core.app.ActivityCompat.shouldShowRequestPermissionRation
import static java.lang.Boolean.TRUE; import static java.lang.Boolean.TRUE;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.briar.android.util.UiUtils.getGoToSettingsListener; import static org.briarproject.briar.android.util.PermissionUtils.isLocationEnabledForWiFi;
import static org.briarproject.briar.android.util.UiUtils.showLocationDialog; import static org.briarproject.briar.android.util.PermissionUtils.showLocationDialog;
/** /**
* This class ensures that the conditions to open a hotspot are fulfilled on * This class ensures that the conditions to open a hotspot are fulfilled on
@@ -47,6 +47,7 @@ class ConditionManager29 extends AbstractConditionManager {
ConditionManager29(ActivityResultCaller arc, ConditionManager29(ActivityResultCaller arc,
Consumer<Boolean> permissionUpdateCallback) { Consumer<Boolean> permissionUpdateCallback) {
super(permissionUpdateCallback); super(permissionUpdateCallback);
// permissionUpdateCallback receives false if permissions were denied
locationRequest = arc.registerForActivityResult( locationRequest = arc.registerForActivityResult(
new RequestPermission(), granted -> { new RequestPermission(), granted -> {
onRequestPermissionResult(granted); onRequestPermissionResult(granted);
@@ -66,7 +67,7 @@ class ConditionManager29 extends AbstractConditionManager {
private boolean areEssentialPermissionsGranted() { private boolean areEssentialPermissionsGranted() {
boolean isWifiEnabled = wifiManager.isWifiEnabled(); boolean isWifiEnabled = wifiManager.isWifiEnabled();
boolean isLocationEnabled = isLocationEnabled(); boolean isLocationEnabled = isLocationEnabledForWiFi(ctx);
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
LOG.info(String.format("areEssentialPermissionsGranted(): " + LOG.info(String.format("areEssentialPermissionsGranted(): " +
"locationPermission? %s, " + "locationPermission? %s, " +
@@ -87,7 +88,7 @@ class ConditionManager29 extends AbstractConditionManager {
return false; return false;
} }
// ensure location is enabled (if needed on this device) // ensure location is enabled (if needed on this device)
if (!isLocationEnabled()) { if (!isLocationEnabledForWiFi(ctx)) {
showLocationDialog(ctx, false); showLocationDialog(ctx, false);
return false; return false;
} }
@@ -98,8 +99,8 @@ class ConditionManager29 extends AbstractConditionManager {
int res = SDK_INT >= 31 ? int res = SDK_INT >= 31 ?
R.string.permission_hotspot_location_denied_precise_body : R.string.permission_hotspot_location_denied_precise_body :
R.string.permission_hotspot_location_denied_body; R.string.permission_hotspot_location_denied_body;
showDenialDialog(ctx, R.string.permission_location_title, res, PermissionUtils.showDenialDialog(ctx,
getGoToSettingsListener(ctx), R.string.permission_location_title, res,
() -> permissionUpdateCallback.accept(false)); () -> permissionUpdateCallback.accept(false));
return false; return false;
} }
@@ -149,12 +150,4 @@ class ConditionManager29 extends AbstractConditionManager {
wifiRequest.launch(new Intent(Settings.Panel.ACTION_WIFI)); wifiRequest.launch(new Intent(Settings.Panel.ACTION_WIFI));
} }
private boolean isLocationEnabled() {
if (SDK_INT >= 31) {
LocationManager lm = ctx.getSystemService(LocationManager.class);
return lm.isLocationEnabled();
}
return true;
}
} }

View File

@@ -14,8 +14,8 @@ import static android.Manifest.permission.CAMERA;
import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.PackageManager.PERMISSION_GRANTED;
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.showDenialDialog; import static org.briarproject.briar.android.util.PermissionUtils.showDenialDialog;
import static org.briarproject.briar.android.util.UiUtils.showRationale; import static org.briarproject.briar.android.util.PermissionUtils.showRationale;
class CameraPermissionManager { class CameraPermissionManager {

View File

@@ -53,13 +53,12 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY;
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED;
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA;
import static org.briarproject.bramble.util.AndroidUtils.hasBtConnectPermission; import static org.briarproject.briar.android.util.PermissionUtils.areBluetoothPermissionsGranted;
import static org.briarproject.bramble.util.AndroidUtils.hasBtScanPermission; import static org.briarproject.briar.android.util.PermissionUtils.requestBluetoothPermissions;
import static org.briarproject.briar.android.util.UiUtils.requestBluetoothPermissions; import static org.briarproject.briar.android.util.PermissionUtils.showDenialDialog;
import static org.briarproject.briar.android.util.UiUtils.showDenialDialog; import static org.briarproject.briar.android.util.PermissionUtils.showRationale;
import static org.briarproject.briar.android.util.PermissionUtils.wasGrantedBluetoothPermissions;
import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog; import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog;
import static org.briarproject.briar.android.util.UiUtils.showRationale;
import static org.briarproject.briar.android.util.UiUtils.wasGrantedBluetoothPermissions;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -220,8 +219,7 @@ public class TransportsActivity extends BriarActivity {
} }
private void onClicked(TransportId transportId, boolean enable) { private void onClicked(TransportId transportId, boolean enable) {
if (enable && SDK_INT >= 31 && if (enable && SDK_INT >= 31 && !areBluetoothPermissionsGranted(this)) {
(!hasBtConnectPermission(this) || !hasBtScanPermission(this))) {
if (shouldShowRequestPermissionRationale(BLUETOOTH_CONNECT)) { if (shouldShowRequestPermissionRationale(BLUETOOTH_CONNECT)) {
showRationale(this, R.string.permission_bluetooth_title, showRationale(this, R.string.permission_bluetooth_title,
R.string.permission_bluetooth_body, R.string.permission_bluetooth_body,
@@ -354,9 +352,10 @@ public class TransportsActivity extends BriarActivity {
@RequiresApi(31) @RequiresApi(31)
private void handleBtPermissionResult(Map<String, Boolean> grantedMap) { private void handleBtPermissionResult(Map<String, Boolean> grantedMap) {
if (wasGrantedBluetoothPermissions(grantedMap)) { if (wasGrantedBluetoothPermissions(this, grantedMap)) {
viewModel.enableTransport(BluetoothConstants.ID, true); viewModel.enableTransport(BluetoothConstants.ID, true);
} else { } else {
// update adapter to reflect the "off" toggle state after denying
transportsAdapter.notifyDataSetChanged(); transportsAdapter.notifyDataSetChanged();
showDenialDialog(this, showDenialDialog(this,
R.string.permission_bluetooth_title, R.string.permission_bluetooth_title,

View File

@@ -61,10 +61,10 @@ import static java.util.Objects.requireNonNull;
import static java.util.TimeZone.getTimeZone; import static java.util.TimeZone.getTimeZone;
import static org.briarproject.bramble.util.AndroidUtils.getBluetoothAddressAndMethod; import static org.briarproject.bramble.util.AndroidUtils.getBluetoothAddressAndMethod;
import static org.briarproject.bramble.util.AndroidUtils.hasBtConnectPermission; import static org.briarproject.bramble.util.AndroidUtils.hasBtConnectPermission;
import static org.briarproject.bramble.util.AndroidUtils.hasBtScanPermission;
import static org.briarproject.bramble.util.PrivacyUtils.scrubInetAddress; import static org.briarproject.bramble.util.PrivacyUtils.scrubInetAddress;
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress; import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
import static org.briarproject.briar.android.util.PermissionUtils.areBluetoothPermissionsGranted;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
@@ -289,7 +289,8 @@ class BriarReportCollector {
// Is Bluetooth connectable? // Is Bluetooth connectable?
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
int scanMode = hasBtScanPermission(ctx) ? bt.getScanMode() : -1; int scanMode = areBluetoothPermissionsGranted(ctx) ?
bt.getScanMode() : -1;
boolean btConnectable = scanMode == SCAN_MODE_CONNECTABLE || boolean btConnectable = scanMode == SCAN_MODE_CONNECTABLE ||
scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE; scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE;
connectivityInfo.add("BluetoothConnectable", btConnectable); connectivityInfo.add("BluetoothConnectable", btConnectable);

View File

@@ -26,14 +26,13 @@ import androidx.preference.SwitchPreferenceCompat;
import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION.SDK_INT;
import static org.briarproject.bramble.util.AndroidUtils.hasBtConnectPermission;
import static org.briarproject.bramble.util.AndroidUtils.hasBtScanPermission;
import static org.briarproject.briar.android.AppModule.getAndroidComponent; import static org.briarproject.briar.android.AppModule.getAndroidComponent;
import static org.briarproject.briar.android.settings.SettingsActivity.enableAndPersist; import static org.briarproject.briar.android.settings.SettingsActivity.enableAndPersist;
import static org.briarproject.briar.android.util.UiUtils.requestBluetoothPermissions; import static org.briarproject.briar.android.util.PermissionUtils.areBluetoothPermissionsGranted;
import static org.briarproject.briar.android.util.UiUtils.showDenialDialog; import static org.briarproject.briar.android.util.PermissionUtils.requestBluetoothPermissions;
import static org.briarproject.briar.android.util.UiUtils.showRationale; import static org.briarproject.briar.android.util.PermissionUtils.showDenialDialog;
import static org.briarproject.briar.android.util.UiUtils.wasGrantedBluetoothPermissions; import static org.briarproject.briar.android.util.PermissionUtils.showRationale;
import static org.briarproject.briar.android.util.PermissionUtils.wasGrantedBluetoothPermissions;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -91,7 +90,7 @@ public class ConnectionsFragment extends PreferenceFragmentCompat {
if (SDK_INT >= 31) { if (SDK_INT >= 31) {
enableBluetooth.setOnPreferenceChangeListener((p, value) -> { enableBluetooth.setOnPreferenceChangeListener((p, value) -> {
FragmentActivity ctx = requireActivity(); FragmentActivity ctx = requireActivity();
if (hasBtConnectPermission(ctx) && hasBtScanPermission(ctx)) { if (areBluetoothPermissionsGranted(ctx)) {
return true; return true;
} else if (shouldShowRequestPermissionRationale( } else if (shouldShowRequestPermissionRationale(
BLUETOOTH_CONNECT)) { BLUETOOTH_CONNECT)) {
@@ -160,7 +159,7 @@ public class ConnectionsFragment extends PreferenceFragmentCompat {
@RequiresApi(31) @RequiresApi(31)
private void handleBtPermissionResult(Map<String, Boolean> grantedMap) { private void handleBtPermissionResult(Map<String, Boolean> grantedMap) {
if (wasGrantedBluetoothPermissions(grantedMap)) { if (wasGrantedBluetoothPermissions(requireActivity(), grantedMap)) {
enableBluetooth.setChecked(true); enableBluetooth.setChecked(true);
} else { } else {
showDenialDialog(requireActivity(), showDenialDialog(requireActivity(),

View File

@@ -0,0 +1,176 @@
package org.briarproject.briar.android.util;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.LocationManager;
import android.net.Uri;
import android.widget.Toast;
import org.briarproject.briar.R;
import org.briarproject.nullsafety.MethodsNotNullByDefault;
import org.briarproject.nullsafety.ParametersNotNullByDefault;
import java.util.Map;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity;
import static android.Manifest.permission.BLUETOOTH_ADVERTISE;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_SCAN;
import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION.SDK_INT;
import static android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS;
import static android.widget.Toast.LENGTH_LONG;
import static androidx.core.content.ContextCompat.checkSelfPermission;
import static java.lang.Boolean.TRUE;
import static org.briarproject.briar.BuildConfig.APPLICATION_ID;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class PermissionUtils {
public static boolean gotPermission(Context ctx,
@Nullable Map<String, Boolean> grantedMap, String permission) {
if (grantedMap == null || !grantedMap.containsKey(permission)) {
return isPermissionGranted(ctx, permission);
} else {
return TRUE.equals(grantedMap.get(permission));
}
}
public static boolean isPermissionGranted(Context ctx, String permission) {
return checkSelfPermission(ctx, permission) ==
PERMISSION_GRANTED;
}
public static boolean areBluetoothPermissionsGranted(Context ctx) {
if (SDK_INT < 31) return true;
return isPermissionGranted(ctx, BLUETOOTH_ADVERTISE) &&
isPermissionGranted(ctx, BLUETOOTH_CONNECT) &&
isPermissionGranted(ctx, BLUETOOTH_SCAN);
}
@RequiresApi(31)
public static boolean wasGrantedBluetoothPermissions(Context ctx,
@Nullable Map<String, Boolean> grantedMap) {
return grantedMap != null &&
gotPermission(ctx, grantedMap, BLUETOOTH_ADVERTISE) &&
gotPermission(ctx, grantedMap, BLUETOOTH_CONNECT) &&
gotPermission(ctx, grantedMap, BLUETOOTH_SCAN);
}
public static DialogInterface.OnClickListener getGoToSettingsListener(
Context context) {
return (dialog, which) -> {
Intent i = new Intent();
i.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
i.addCategory(CATEGORY_DEFAULT);
i.setData(Uri.parse("package:" + APPLICATION_ID));
i.addFlags(FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
};
}
/**
* @return true if location is enabled for Bluetooth purposes,
* or it isn't required due to this being a device with SDK < 28 or >= 31.
*/
public static boolean isLocationEnabledForBt(Context ctx) {
if (SDK_INT >= 28 && SDK_INT < 31) {
LocationManager lm = ctx.getSystemService(LocationManager.class);
return lm.isLocationEnabled();
} else {
return true;
}
}
/**
* @return true if location is enabled for Wi-Fi hotspot purposes,
* or it isn't required due to this being a device with SDK < 31.
*/
public static boolean isLocationEnabledForWiFi(Context ctx) {
if (SDK_INT >= 31) {
LocationManager lm = ctx.getSystemService(LocationManager.class);
return lm.isLocationEnabled();
}
return true;
}
public static void showLocationDialog(Context ctx) {
showLocationDialog(ctx, true);
}
public static void showLocationDialog(Context ctx, boolean forBluetooth) {
AlertDialog.Builder builder =
new AlertDialog.Builder(ctx, R.style.BriarDialogTheme);
builder.setTitle(R.string.permission_location_setting_title);
if (forBluetooth) {
builder.setMessage(R.string.permission_location_setting_body);
} else {
builder.setMessage(
R.string.permission_location_setting_hotspot_body);
}
builder.setNegativeButton(R.string.cancel, null);
builder.setPositiveButton(R.string.permission_location_setting_button,
(dialog, which) -> {
Intent i = new Intent(ACTION_LOCATION_SOURCE_SETTINGS);
try {
ctx.startActivity(i);
} catch (ActivityNotFoundException e) {
Toast.makeText(ctx, R.string.error_start_activity,
LENGTH_LONG).show();
}
});
builder.show();
}
/**
* This closes the given activity when dialog gets cancelled.
*/
public static void showDenialDialog(FragmentActivity ctx,
@StringRes int title, @StringRes int body) {
showDenialDialog(ctx, title, body, ctx::supportFinishAfterTransition);
}
public static void showDenialDialog(FragmentActivity ctx,
@StringRes int title, @StringRes int body, Runnable onDenied) {
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) ->
onDenied.run());
builder.show();
}
public static void showRationale(FragmentActivity ctx, @StringRes int title,
@StringRes int body, @Nullable Runnable onOk) {
AlertDialog.Builder builder =
new AlertDialog.Builder(ctx, R.style.BriarDialogTheme);
builder.setTitle(title);
builder.setMessage(body);
builder.setNeutralButton(R.string.continue_button, (dialog, which) -> {
if (onOk != null) onOk.run();
dialog.dismiss();
});
builder.show();
}
@RequiresApi(31)
public static void requestBluetoothPermissions(
ActivityResultLauncher<String[]> launcher) {
String[] perms = new String[] {BLUETOOTH_ADVERTISE, BLUETOOTH_CONNECT,
BLUETOOTH_SCAN};
launcher.launch(perms);
}
}

View File

@@ -6,12 +6,9 @@ import android.app.ActivityManager.MemoryInfo;
import android.app.KeyguardManager; import android.app.KeyguardManager;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent; import android.content.Intent;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Debug; import android.os.Debug;
import android.text.Spannable; import android.text.Spannable;
import android.text.SpannableString; import android.text.SpannableString;
@@ -44,7 +41,6 @@ import org.briarproject.nullsafety.MethodsNotNullByDefault;
import org.briarproject.nullsafety.ParametersNotNullByDefault; import org.briarproject.nullsafety.ParametersNotNullByDefault;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.logging.Logger; import java.util.logging.Logger;
import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.ActivityResultLauncher;
@@ -55,7 +51,6 @@ 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;
@@ -71,15 +66,10 @@ import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
import static android.Manifest.permission.BLUETOOTH_ADVERTISE;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_SCAN;
import static android.content.Context.KEYGUARD_SERVICE; import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.Build.MANUFACTURER; import static android.os.Build.MANUFACTURER;
import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION.SDK_INT;
import static android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS;
import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.FORMAT_ABBREV_ALL; import static android.text.format.DateUtils.FORMAT_ABBREV_ALL;
import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
@@ -109,13 +99,11 @@ import static androidx.core.content.ContextCompat.getColor;
import static androidx.core.content.ContextCompat.getSystemService; import static androidx.core.content.ContextCompat.getSystemService;
import static androidx.core.graphics.drawable.DrawableCompat.setTint; import static androidx.core.graphics.drawable.DrawableCompat.setTint;
import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_RTL; import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_RTL;
import static java.lang.Boolean.TRUE;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.DAYS; import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.briar.BuildConfig.APPLICATION_ID;
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE; import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
import static org.briarproject.briar.android.TestingConstants.IS_OLD_ANDROID; import static org.briarproject.briar.android.TestingConstants.IS_OLD_ANDROID;
import static org.briarproject.briar.android.TestingConstants.OLD_ANDROID_WARN_DATE; import static org.briarproject.briar.android.TestingConstants.OLD_ANDROID_WARN_DATE;
@@ -330,17 +318,6 @@ public class UiUtils {
textView.setMovementMethod(new LinkMovementMethod()); textView.setMovementMethod(new LinkMovementMethod());
} }
public static OnClickListener getGoToSettingsListener(Context context) {
return (dialog, which) -> {
Intent i = new Intent();
i.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
i.addCategory(CATEGORY_DEFAULT);
i.setData(Uri.parse("package:" + APPLICATION_ID));
i.addFlags(FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
};
}
public static void showOnboardingDialog(Context ctx, String text) { public static void showOnboardingDialog(Context ctx, String text) {
new AlertDialog.Builder(ctx, R.style.OnboardingDialogTheme) new AlertDialog.Builder(ctx, R.style.OnboardingDialogTheme)
.setMessage(text) .setMessage(text)
@@ -349,19 +326,6 @@ public class UiUtils {
.show(); .show();
} }
/**
* @return true if location is enabled,
* or it isn't required due to this being a device with SDK < 28 or >= 31.
*/
public static boolean isLocationEnabled(Context ctx) {
if (SDK_INT >= 28 && SDK_INT < 31) {
LocationManager lm = ctx.getSystemService(LocationManager.class);
return lm.isLocationEnabled();
} else {
return true;
}
}
public static boolean isSamsung7() { public static boolean isSamsung7() {
return (SDK_INT == 24 || SDK_INT == 25) && return (SDK_INT == 24 || SDK_INT == 25) &&
MANUFACTURER.equalsIgnoreCase("Samsung"); MANUFACTURER.equalsIgnoreCase("Samsung");
@@ -528,34 +492,6 @@ public class UiUtils {
return isoCode; return isoCode;
} }
public static void showLocationDialog(Context ctx) {
showLocationDialog(ctx, true);
}
public static void showLocationDialog(Context ctx, boolean forBluetooth) {
AlertDialog.Builder builder =
new AlertDialog.Builder(ctx, R.style.BriarDialogTheme);
builder.setTitle(R.string.permission_location_setting_title);
if (forBluetooth) {
builder.setMessage(R.string.permission_location_setting_body);
} else {
builder.setMessage(
R.string.permission_location_setting_hotspot_body);
}
builder.setNegativeButton(R.string.cancel, null);
builder.setPositiveButton(R.string.permission_location_setting_button,
(dialog, which) -> {
Intent i = new Intent(ACTION_LOCATION_SOURCE_SETTINGS);
try {
ctx.startActivity(i);
} catch (ActivityNotFoundException e) {
Toast.makeText(ctx, R.string.error_start_activity,
LENGTH_LONG).show();
}
});
builder.show();
}
public static Drawable getDialogIcon(Context ctx, @DrawableRes int resId) { public static Drawable getDialogIcon(Context ctx, @DrawableRes int resId) {
Drawable icon = Drawable icon =
VectorDrawableCompat.create(ctx.getResources(), resId, null); VectorDrawableCompat.create(ctx.getResources(), resId, null);
@@ -595,29 +531,6 @@ 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();
}
public static void launchActivityToOpenFile(Context ctx, public static void launchActivityToOpenFile(Context ctx,
@Nullable ActivityResultLauncher<String[]> docLauncher, @Nullable ActivityResultLauncher<String[]> docLauncher,
ActivityResultLauncher<String> contentLauncher, ActivityResultLauncher<String> contentLauncher,
@@ -640,20 +553,4 @@ public class UiUtils {
Toast.makeText(ctx, R.string.error_start_activity, LENGTH_LONG).show(); Toast.makeText(ctx, R.string.error_start_activity, LENGTH_LONG).show();
} }
@RequiresApi(31)
public static void requestBluetoothPermissions(
ActivityResultLauncher<String[]> launcher) {
String[] perms = new String[] {BLUETOOTH_ADVERTISE, BLUETOOTH_CONNECT,
BLUETOOTH_SCAN};
launcher.launch(perms);
}
@RequiresApi(31)
public static boolean wasGrantedBluetoothPermissions(
@Nullable Map<String, Boolean> grantedMap) {
return grantedMap != null &&
TRUE.equals(grantedMap.get(BLUETOOTH_ADVERTISE)) &&
TRUE.equals(grantedMap.get(BLUETOOTH_CONNECT)) &&
TRUE.equals(grantedMap.get(BLUETOOTH_SCAN));
}
} }

View File

@@ -353,6 +353,7 @@
<string name="connect_via_bluetooth_intro">In case Bluetooth connections do not work automatically, you can use this screen to connect manually.\n\nYour contact needs to be nearby for this to work.\n\nYou and your contact should both press \"Start\" at the same time.</string> <string name="connect_via_bluetooth_intro">In case Bluetooth connections do not work automatically, you can use this screen to connect manually.\n\nYour contact needs to be nearby for this to work.\n\nYou and your contact should both press \"Start\" at the same time.</string>
<string name="connect_via_bluetooth_already_discovering">Already trying to connect via Bluetooth. Please try again shortly.</string> <string name="connect_via_bluetooth_already_discovering">Already trying to connect via Bluetooth. Please try again shortly.</string>
<string name="connect_via_bluetooth_no_location_permission">Cannot continue without location permission</string> <string name="connect_via_bluetooth_no_location_permission">Cannot continue without location permission</string>
<string name="connect_via_bluetooth_no_bluetooth_permission">Cannot continue without nearby devices permission</string>
<string name="connect_via_bluetooth_start">Connecting via Bluetooth…</string> <string name="connect_via_bluetooth_start">Connecting via Bluetooth…</string>
<string name="connect_via_bluetooth_success">Successfully connected via Bluetooth</string> <string name="connect_via_bluetooth_success">Successfully connected via Bluetooth</string>
<string name="connect_via_bluetooth_error">Could not connect via Bluetooth.</string> <string name="connect_via_bluetooth_error">Could not connect via Bluetooth.</string>