diff --git a/briar-android/src/main/AndroidManifest.xml b/briar-android/src/main/AndroidManifest.xml index 873971ae8..04fa06497 100644 --- a/briar-android/src/main/AndroidManifest.xml +++ b/briar-android/src/main/AndroidManifest.xml @@ -19,6 +19,10 @@ + @@ -32,7 +36,8 @@ tools:ignore="ScopedStorage" /> - + 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 64e399605..d243dd320 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 @@ -28,7 +28,7 @@ import static org.briarproject.briar.android.util.PermissionUtils.showLocationDi /** * This class ensures that the conditions to open a hotspot are fulfilled on - * API levels >= 29. + * API levels >= 29 and < 33. *

* As soon as {@link #checkAndRequestConditions()} returns true, * all conditions are fulfilled. diff --git a/briar-android/src/main/java/org/briarproject/briar/android/hotspot/ConditionManager33.java b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/ConditionManager33.java new file mode 100644 index 000000000..13a95aa84 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/ConditionManager33.java @@ -0,0 +1,139 @@ +package org.briarproject.briar.android.hotspot; + +import android.content.Intent; +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; + +import androidx.activity.result.ActivityResultCaller; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts.RequestPermission; +import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import androidx.core.util.Consumer; + +import static android.Manifest.permission.NEARBY_WIFI_DEVICES; +import static androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale; +import static java.lang.Boolean.TRUE; +import static java.util.logging.Level.INFO; +import static java.util.logging.Logger.getLogger; + +/** + * This class ensures that the conditions to open a hotspot are fulfilled on + * API levels >= 33. + *

+ * As soon as {@link #checkAndRequestConditions()} returns true, + * all conditions are fulfilled. + */ +@RequiresApi(33) +class ConditionManager33 extends AbstractConditionManager { + + private static final Logger LOG = + getLogger(ConditionManager33.class.getName()); + + private Permission nearbyWifiPermission = Permission.UNKNOWN; + + private final ActivityResultLauncher nearbyWifiRequest; + private final ActivityResultLauncher wifiRequest; + + ConditionManager33(ActivityResultCaller arc, + Consumer permissionUpdateCallback) { + super(permissionUpdateCallback); + // permissionUpdateCallback receives false if permissions were denied + nearbyWifiRequest = arc.registerForActivityResult( + new RequestPermission(), granted -> { + onRequestPermissionResult(granted); + permissionUpdateCallback.accept(TRUE.equals(granted)); + }); + wifiRequest = arc.registerForActivityResult( + new StartActivityForResult(), + result -> permissionUpdateCallback + .accept(wifiManager.isWifiEnabled()) + ); + } + + @Override + void onStart() { + nearbyWifiPermission = Permission.UNKNOWN; + } + + private boolean areEssentialPermissionsGranted() { + boolean isWifiEnabled = wifiManager.isWifiEnabled(); + if (LOG.isLoggable(INFO)) { + LOG.info(String.format("areEssentialPermissionsGranted(): " + + "nearbyWifiPermission? %s, " + + "wifiManager.isWifiEnabled()? %b", + nearbyWifiPermission, isWifiEnabled)); + } + return nearbyWifiPermission == Permission.GRANTED && isWifiEnabled; + } + + @Override + boolean checkAndRequestConditions() { + if (areEssentialPermissionsGranted()) return true; + + if (nearbyWifiPermission == Permission.UNKNOWN) { + requestPermissions(); + return false; + } + + // If the location permission has been permanently denied, ask the + // user to change the setting + if (nearbyWifiPermission == Permission.PERMANENTLY_DENIED) { + PermissionUtils.showDenialDialog(ctx, + R.string.permission_nearby_devices_title, + R.string.permission_hotspot_nearby_wifi_denied_body, + () -> permissionUpdateCallback.accept(false)); + return false; + } + + // Should we show the rationale for location permission? + if (nearbyWifiPermission == Permission.SHOW_RATIONALE) { + showRationale(ctx, + R.string.permission_location_title, + R.string.permission_hotspot_nearby_wifi_request_body, + this::requestPermissions, + () -> permissionUpdateCallback.accept(false)); + return false; + } + + // If Wifi is not enabled, we show the rationale for enabling Wifi? + if (!wifiManager.isWifiEnabled()) { + showRationale(ctx, R.string.wifi_settings_title, + R.string.wifi_settings_request_enable_body, + this::requestEnableWiFi, + () -> permissionUpdateCallback.accept(false)); + return false; + } + + // we shouldn't usually reach this point, but if we do, return false + // anyway to force a recheck. Maybe some condition changed in the + // meantime. + return false; + } + + private void onRequestPermissionResult(@Nullable Boolean granted) { + if (granted != null && granted) { + nearbyWifiPermission = Permission.GRANTED; + } else if (shouldShowRequestPermissionRationale(ctx, + NEARBY_WIFI_DEVICES)) { + nearbyWifiPermission = Permission.SHOW_RATIONALE; + } else { + nearbyWifiPermission = Permission.PERMANENTLY_DENIED; + } + } + + private void requestPermissions() { + nearbyWifiRequest.launch(NEARBY_WIFI_DEVICES); + } + + private void requestEnableWiFi() { + wifiRequest.launch(new Intent(Settings.Panel.ACTION_WIFI)); + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/hotspot/HotspotIntroFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/HotspotIntroFragment.java index c7e9bcd30..fbce088d4 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/hotspot/HotspotIntroFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/HotspotIntroFragment.java @@ -50,7 +50,9 @@ public class HotspotIntroFragment extends Fragment { private final AbstractConditionManager conditionManager = SDK_INT < 29 ? new ConditionManager(this, this::onPermissionUpdate) : - new ConditionManager29(this, this::onPermissionUpdate); + SDK_INT >= 33 ? + new ConditionManager33(this, this::onPermissionUpdate) : + new ConditionManager29(this, this::onPermissionUpdate); @Override public void onAttach(Context context) { diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index c108f88d5..4719ed5f2 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -789,6 +789,7 @@ Camera permission To scan the QR code, Briar needs access to the camera. Location permission + Nearby devices permission To discover Bluetooth devices, Briar needs permission to access your location.\n\nBriar does not store your location or share it with anyone. Camera and location To scan the QR code, Briar needs access to the camera.\n\nTo discover Bluetooth devices, Briar needs permission to access your location.\n\nBriar does not store your location or share it with anyone. @@ -833,6 +834,8 @@ To create a Wi-Fi hotspot, Briar needs permission to access your precise location.\n\nBriar does not store your location or share it with anyone. You have denied access to your location, but Briar needs this permission to create a Wi-Fi hotspot.\n\nPlease consider granting access. You have denied access to your precise location, but Briar needs this permission to create a Wi-Fi hotspot.\n\nPlease consider granting access. + To create a Wi-Fi hotspot, Briar needs permission to access nearby devices. + You have denied access to nearby devices, but Briar needs this permission to create a Wi-Fi hotspot.\n\nPlease consider granting access. Wi-Fi setting To create a Wi-Fi hotspot, Briar needs to use Wi-Fi. Please enable it.