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

@@ -19,17 +19,17 @@ 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.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.PERMANENTLY_DENIED;
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.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;
import static org.briarproject.briar.android.util.UiUtils.wasGrantedBluetoothPermissions;
import static org.briarproject.briar.android.util.PermissionUtils.areBluetoothPermissionsGranted;
import static org.briarproject.briar.android.util.PermissionUtils.gotPermission;
import static org.briarproject.briar.android.util.PermissionUtils.isLocationEnabledForBt;
import static org.briarproject.briar.android.util.PermissionUtils.showDenialDialog;
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 {
@@ -64,9 +64,7 @@ class AddNearbyContactPermissionManager {
} else if (SDK_INT < 31) {
bluetoothOk = checkSelfPermission(ctx, ACCESS_FINE_LOCATION) == ok;
} else {
bluetoothOk = hasBtConnectPermission(ctx) &&
hasBtScanPermission(ctx) &&
checkSelfPermission(ctx, BLUETOOTH_ADVERTISE) == ok;
bluetoothOk = areBluetoothPermissionsGranted(ctx);
}
return bluetoothOk && checkSelfPermission(ctx, CAMERA) == ok;
}
@@ -79,7 +77,7 @@ class AddNearbyContactPermissionManager {
}
boolean checkPermissions() {
boolean locationEnabled = isLocationEnabled(ctx);
boolean locationEnabled = isLocationEnabledForBt(ctx);
if (locationEnabled && areEssentialPermissionsGranted()) return true;
// If an essential permission has been permanently denied, ask the
// user to change the setting
@@ -141,7 +139,7 @@ class AddNearbyContactPermissionManager {
}
void onRequestPermissionResult(Map<String, Boolean> result) {
if (gotPermission(CAMERA, result)) {
if (gotPermission(ctx, result, CAMERA)) {
cameraPermission = GRANTED;
} else if (shouldShowRationale(CAMERA)) {
cameraPermission = SHOW_RATIONALE;
@@ -150,7 +148,7 @@ class AddNearbyContactPermissionManager {
}
if (isBluetoothSupported) {
if (SDK_INT < 31) {
if (gotPermission(ACCESS_FINE_LOCATION, result)) {
if (gotPermission(ctx, result, ACCESS_FINE_LOCATION)) {
locationPermission = GRANTED;
} else if (shouldShowRationale(ACCESS_FINE_LOCATION)) {
locationPermission = SHOW_RATIONALE;
@@ -158,7 +156,7 @@ class AddNearbyContactPermissionManager {
locationPermission = PERMANENTLY_DENIED;
}
} else {
if (wasGrantedBluetoothPermissions(result)) {
if (wasGrantedBluetoothPermissions(ctx, result)) {
bluetoothPermissions = GRANTED;
} else if (shouldShowRationale(BLUETOOTH_CONNECT)) {
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) {
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.REFUSED;
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.isLocationEnabled;
@NotNullByDefault
class AddNearbyContactViewModel extends AndroidViewModel
@@ -396,7 +396,7 @@ class AddNearbyContactViewModel extends AndroidViewModel
void showQrCodeFragmentIfAllowed() {
boolean permissionsGranted = areEssentialPermissionsGranted(
getApplication(), isBluetoothSupported());
boolean locationEnabled = isLocationEnabled(getApplication());
boolean locationEnabled = isLocationEnabledForBt(getApplication());
if (wasContinueClicked && permissionsGranted && locationEnabled) {
if (isWifiReady() && isBluetoothReady()) {
LOG.info("Wifi and Bluetooth are ready");

View File

@@ -1,35 +1,34 @@
package org.briarproject.briar.android.contact.connect;
import android.app.Activity;
import android.content.Context;
import android.widget.Toast;
import org.briarproject.briar.R;
import org.briarproject.briar.android.util.Permission;
import org.briarproject.briar.android.util.UiUtils;
import java.util.Map;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
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.widget.Toast.LENGTH_LONG;
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.PERMANENTLY_DENIED;
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.UiUtils.getGoToSettingsListener;
import static org.briarproject.briar.android.util.UiUtils.isLocationEnabled;
import static org.briarproject.briar.android.util.UiUtils.requestBluetoothPermissions;
import static org.briarproject.briar.android.util.UiUtils.showLocationDialog;
import static org.briarproject.briar.android.util.UiUtils.wasGrantedBluetoothPermissions;
import static org.briarproject.briar.android.util.PermissionUtils.gotPermission;
import static org.briarproject.briar.android.util.PermissionUtils.isLocationEnabledForBt;
import static org.briarproject.briar.android.util.PermissionUtils.requestBluetoothPermissions;
import static org.briarproject.briar.android.util.PermissionUtils.showDenialDialog;
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 {
@@ -48,7 +47,7 @@ class BluetoothConditionManager {
@UiThread
void requestPermissions(ActivityResultLauncher<String[]> launcher) {
if (SDK_INT < 31) {
launcher.launch(new String[] {ACCESS_FINE_LOCATION});
requestLocationPermission(launcher);
} else {
requestBluetoothPermissions(launcher);
}
@@ -58,7 +57,7 @@ class BluetoothConditionManager {
void onLocationPermissionResult(Activity activity,
@Nullable Map<String, Boolean> result) {
if (SDK_INT < 31) {
if (gotPermission(activity, result)) {
if (gotPermission(activity, result, ACCESS_FINE_LOCATION)) {
locationPermission = GRANTED;
} else if (shouldShowRequestPermissionRationale(activity,
ACCESS_FINE_LOCATION)) {
@@ -67,7 +66,7 @@ class BluetoothConditionManager {
locationPermission = PERMANENTLY_DENIED;
}
} else {
if (wasGrantedBluetoothPermissions(result)) {
if (wasGrantedBluetoothPermissions(activity, result)) {
bluetoothPermissions = GRANTED;
} else if (shouldShowRequestPermissionRationale(activity,
BLUETOOTH_CONNECT)) {
@@ -84,58 +83,38 @@ class BluetoothConditionManager {
boolean permissionGranted =
(SDK_INT < 23 || locationPermission == GRANTED) &&
bluetoothPermissions == GRANTED;
boolean locationEnabled = isLocationEnabled(ctx);
boolean locationEnabled = isLocationEnabledForBt(ctx);
if (permissionGranted && locationEnabled) return true;
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) {
showRationale(ctx, permissionRequest);
showRationale(ctx, R.string.permission_location_title,
R.string.permission_location_request_body,
() -> requestLocationPermission(permissionRequest));
} else if (!locationEnabled) {
showLocationDialog(ctx);
} else if (bluetoothPermissions == PERMANENTLY_DENIED) {
UiUtils.showDenialDialog(ctx, R.string.permission_bluetooth_title,
R.string.permission_bluetooth_denied_body);
Runnable onDenied = () -> Toast.makeText(ctx,
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) {
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, () ->
requestBluetoothPermissions(permissionRequest));
}
return false;
}
private void showDenialDialog(Context ctx, Runnable onLocationDenied) {
new AlertDialog.Builder(ctx, R.style.BriarDialogTheme)
.setTitle(R.string.permission_location_title)
.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;
private void requestLocationPermission(
ActivityResultLauncher<String[]> launcher) {
launcher.launch(new String[] {ACCESS_FINE_LOCATION});
}
}

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.util.LogUtils.logException;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
import static org.briarproject.briar.android.util.PermissionUtils.areBluetoothPermissionsGranted;
@UiThread
@NotNullByDefault
@@ -260,7 +261,8 @@ class ConnectViaBluetoothViewModel extends DbViewModel implements
private void stopConnecting() {
BluetoothPlugin bluetoothPlugin = this.bluetoothPlugin;
if (bluetoothPlugin != null) {
if (bluetoothPlugin != null &&
areBluetoothPermissionsGranted(getApplication())) {
bluetoothPlugin.stopDiscoverAndConnect();
}
}

View File

@@ -1,7 +1,6 @@
package org.briarproject.briar.android.hotspot;
import android.content.Context;
import android.content.DialogInterface;
import android.net.wifi.WifiManager;
import org.briarproject.briar.R;
@@ -20,6 +19,12 @@ import static android.content.Context.WIFI_SERVICE;
*/
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;
protected FragmentActivity ctx;
WifiManager wifiManager;
@@ -52,19 +57,6 @@ abstract class AbstractConditionManager {
*/
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,
@StringRes int body, Runnable onContinueClicked,
Runnable onDismiss) {

View File

@@ -1,11 +1,11 @@
package org.briarproject.briar.android.hotspot;
import android.content.Intent;
import android.location.LocationManager;
import android.provider.Settings;
import org.briarproject.briar.R;
import org.briarproject.briar.android.util.Permission;
import org.briarproject.briar.android.util.PermissionUtils;
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.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.briar.android.util.UiUtils.getGoToSettingsListener;
import static org.briarproject.briar.android.util.UiUtils.showLocationDialog;
import static org.briarproject.briar.android.util.PermissionUtils.isLocationEnabledForWiFi;
import static org.briarproject.briar.android.util.PermissionUtils.showLocationDialog;
/**
* This class ensures that the conditions to open a hotspot are fulfilled on
@@ -47,6 +47,7 @@ class ConditionManager29 extends AbstractConditionManager {
ConditionManager29(ActivityResultCaller arc,
Consumer<Boolean> permissionUpdateCallback) {
super(permissionUpdateCallback);
// permissionUpdateCallback receives false if permissions were denied
locationRequest = arc.registerForActivityResult(
new RequestPermission(), granted -> {
onRequestPermissionResult(granted);
@@ -66,7 +67,7 @@ class ConditionManager29 extends AbstractConditionManager {
private boolean areEssentialPermissionsGranted() {
boolean isWifiEnabled = wifiManager.isWifiEnabled();
boolean isLocationEnabled = isLocationEnabled();
boolean isLocationEnabled = isLocationEnabledForWiFi(ctx);
if (LOG.isLoggable(INFO)) {
LOG.info(String.format("areEssentialPermissionsGranted(): " +
"locationPermission? %s, " +
@@ -87,7 +88,7 @@ class ConditionManager29 extends AbstractConditionManager {
return false;
}
// ensure location is enabled (if needed on this device)
if (!isLocationEnabled()) {
if (!isLocationEnabledForWiFi(ctx)) {
showLocationDialog(ctx, false);
return false;
}
@@ -98,8 +99,8 @@ class ConditionManager29 extends AbstractConditionManager {
int res = SDK_INT >= 31 ?
R.string.permission_hotspot_location_denied_precise_body :
R.string.permission_hotspot_location_denied_body;
showDenialDialog(ctx, R.string.permission_location_title, res,
getGoToSettingsListener(ctx),
PermissionUtils.showDenialDialog(ctx,
R.string.permission_location_title, res,
() -> permissionUpdateCallback.accept(false));
return false;
}
@@ -149,12 +150,4 @@ class ConditionManager29 extends AbstractConditionManager {
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 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;
import static org.briarproject.briar.android.util.PermissionUtils.showDenialDialog;
import static org.briarproject.briar.android.util.PermissionUtils.showRationale;
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_COUNTRY_BLOCKED;
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA;
import static org.briarproject.bramble.util.AndroidUtils.hasBtConnectPermission;
import static org.briarproject.bramble.util.AndroidUtils.hasBtScanPermission;
import static org.briarproject.briar.android.util.UiUtils.requestBluetoothPermissions;
import static org.briarproject.briar.android.util.UiUtils.showDenialDialog;
import static org.briarproject.briar.android.util.PermissionUtils.areBluetoothPermissionsGranted;
import static org.briarproject.briar.android.util.PermissionUtils.requestBluetoothPermissions;
import static org.briarproject.briar.android.util.PermissionUtils.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.showRationale;
import static org.briarproject.briar.android.util.UiUtils.wasGrantedBluetoothPermissions;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -220,8 +219,7 @@ public class TransportsActivity extends BriarActivity {
}
private void onClicked(TransportId transportId, boolean enable) {
if (enable && SDK_INT >= 31 &&
(!hasBtConnectPermission(this) || !hasBtScanPermission(this))) {
if (enable && SDK_INT >= 31 && !areBluetoothPermissionsGranted(this)) {
if (shouldShowRequestPermissionRationale(BLUETOOTH_CONNECT)) {
showRationale(this, R.string.permission_bluetooth_title,
R.string.permission_bluetooth_body,
@@ -354,9 +352,10 @@ public class TransportsActivity extends BriarActivity {
@RequiresApi(31)
private void handleBtPermissionResult(Map<String, Boolean> grantedMap) {
if (wasGrantedBluetoothPermissions(grantedMap)) {
if (wasGrantedBluetoothPermissions(this, grantedMap)) {
viewModel.enableTransport(BluetoothConstants.ID, true);
} else {
// update adapter to reflect the "off" toggle state after denying
transportsAdapter.notifyDataSetChanged();
showDenialDialog(this,
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 org.briarproject.bramble.util.AndroidUtils.getBluetoothAddressAndMethod;
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.scrubMacAddress;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
import static org.briarproject.briar.android.util.PermissionUtils.areBluetoothPermissionsGranted;
@Immutable
@NotNullByDefault
@@ -289,7 +289,8 @@ class BriarReportCollector {
// Is Bluetooth connectable?
@SuppressLint("MissingPermission")
int scanMode = hasBtScanPermission(ctx) ? bt.getScanMode() : -1;
int scanMode = areBluetoothPermissionsGranted(ctx) ?
bt.getScanMode() : -1;
boolean btConnectable = scanMode == SCAN_MODE_CONNECTABLE ||
scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE;
connectivityInfo.add("BluetoothConnectable", btConnectable);

View File

@@ -26,14 +26,13 @@ import androidx.preference.SwitchPreferenceCompat;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
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.settings.SettingsActivity.enableAndPersist;
import static org.briarproject.briar.android.util.UiUtils.requestBluetoothPermissions;
import static org.briarproject.briar.android.util.UiUtils.showDenialDialog;
import static org.briarproject.briar.android.util.UiUtils.showRationale;
import static org.briarproject.briar.android.util.UiUtils.wasGrantedBluetoothPermissions;
import static org.briarproject.briar.android.util.PermissionUtils.areBluetoothPermissionsGranted;
import static org.briarproject.briar.android.util.PermissionUtils.requestBluetoothPermissions;
import static org.briarproject.briar.android.util.PermissionUtils.showDenialDialog;
import static org.briarproject.briar.android.util.PermissionUtils.showRationale;
import static org.briarproject.briar.android.util.PermissionUtils.wasGrantedBluetoothPermissions;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -91,7 +90,7 @@ public class ConnectionsFragment extends PreferenceFragmentCompat {
if (SDK_INT >= 31) {
enableBluetooth.setOnPreferenceChangeListener((p, value) -> {
FragmentActivity ctx = requireActivity();
if (hasBtConnectPermission(ctx) && hasBtScanPermission(ctx)) {
if (areBluetoothPermissionsGranted(ctx)) {
return true;
} else if (shouldShowRequestPermissionRationale(
BLUETOOTH_CONNECT)) {
@@ -160,7 +159,7 @@ public class ConnectionsFragment extends PreferenceFragmentCompat {
@RequiresApi(31)
private void handleBtPermissionResult(Map<String, Boolean> grantedMap) {
if (wasGrantedBluetoothPermissions(grantedMap)) {
if (wasGrantedBluetoothPermissions(requireActivity(), grantedMap)) {
enableBluetooth.setChecked(true);
} else {
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.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Debug;
import android.text.Spannable;
import android.text.SpannableString;
@@ -44,7 +41,6 @@ import org.briarproject.nullsafety.MethodsNotNullByDefault;
import org.briarproject.nullsafety.ParametersNotNullByDefault;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Logger;
import androidx.activity.result.ActivityResultLauncher;
@@ -55,7 +51,6 @@ 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;
@@ -71,15 +66,10 @@ import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
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.Intent.CATEGORY_DEFAULT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.Build.MANUFACTURER;
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.FORMAT_ABBREV_ALL;
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.graphics.drawable.DrawableCompat.setTint;
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.concurrent.TimeUnit.DAYS;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
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.IS_OLD_ANDROID;
import static org.briarproject.briar.android.TestingConstants.OLD_ANDROID_WARN_DATE;
@@ -330,17 +318,6 @@ public class UiUtils {
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) {
new AlertDialog.Builder(ctx, R.style.OnboardingDialogTheme)
.setMessage(text)
@@ -349,19 +326,6 @@ public class UiUtils {
.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() {
return (SDK_INT == 24 || SDK_INT == 25) &&
MANUFACTURER.equalsIgnoreCase("Samsung");
@@ -528,34 +492,6 @@ public class UiUtils {
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) {
Drawable icon =
VectorDrawableCompat.create(ctx.getResources(), resId, null);
@@ -595,29 +531,6 @@ 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();
}
public static void launchActivityToOpenFile(Context ctx,
@Nullable ActivityResultLauncher<String[]> docLauncher,
ActivityResultLauncher<String> contentLauncher,
@@ -640,20 +553,4 @@ public class UiUtils {
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));
}
}