permissionUpdateCallback) {
+ super( permissionUpdateCallback);
}
@Override
@@ -76,8 +67,9 @@ class ConditionManager extends AbstractConditionManager {
return false;
}
- private void requestEnableWiFi() {
- wifiRequest.launch(new Intent(Settings.ACTION_WIFI_SETTINGS));
+ @Override
+ String getWifiSettingsAction() {
+ return Settings.ACTION_WIFI_SETTINGS;
}
}
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..547a44e92 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
@@ -1,18 +1,17 @@
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 org.briarproject.nullsafety.NotNullByDefault;
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;
@@ -28,12 +27,13 @@ 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.
*/
@RequiresApi(29)
+@NotNullByDefault
class ConditionManager29 extends AbstractConditionManager {
private static final Logger LOG =
@@ -42,7 +42,6 @@ class ConditionManager29 extends AbstractConditionManager {
private Permission locationPermission = Permission.UNKNOWN;
private final ActivityResultLauncher locationRequest;
- private final ActivityResultLauncher wifiRequest;
ConditionManager29(ActivityResultCaller arc,
Consumer permissionUpdateCallback) {
@@ -53,11 +52,6 @@ class ConditionManager29 extends AbstractConditionManager {
onRequestPermissionResult(granted);
permissionUpdateCallback.accept(TRUE.equals(granted));
});
- wifiRequest = arc.registerForActivityResult(
- new StartActivityForResult(),
- result -> permissionUpdateCallback
- .accept(wifiManager.isWifiEnabled())
- );
}
@Override
@@ -131,6 +125,11 @@ class ConditionManager29 extends AbstractConditionManager {
return false;
}
+ @Override
+ String getWifiSettingsAction() {
+ return Settings.Panel.ACTION_WIFI;
+ }
+
private void onRequestPermissionResult(@Nullable Boolean granted) {
if (granted != null && granted) {
locationPermission = Permission.GRANTED;
@@ -146,8 +145,4 @@ class ConditionManager29 extends AbstractConditionManager {
locationRequest.launch(ACCESS_FINE_LOCATION);
}
- private void requestEnableWiFi() {
- wifiRequest.launch(new Intent(Settings.Panel.ACTION_WIFI));
- }
-
}
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..89cf62e2b
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/ConditionManager33.java
@@ -0,0 +1,134 @@
+package org.briarproject.briar.android.hotspot;
+
+import android.provider.Settings;
+
+import org.briarproject.briar.R;
+import org.briarproject.briar.android.util.Permission;
+import org.briarproject.briar.android.util.PermissionUtils;
+import org.briarproject.nullsafety.NotNullByDefault;
+
+import java.util.logging.Logger;
+
+import androidx.activity.result.ActivityResultCaller;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts.RequestPermission;
+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)
+@NotNullByDefault
+class ConditionManager33 extends AbstractConditionManager {
+
+ private static final Logger LOG =
+ getLogger(ConditionManager33.class.getName());
+
+ private Permission nearbyWifiPermission = Permission.UNKNOWN;
+
+ private final ActivityResultLauncher nearbyWifiRequest;
+
+ 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));
+ });
+ }
+
+ @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;
+ }
+
+ @Override
+ String getWifiSettingsAction() {
+ return Settings.Panel.ACTION_WIFI;
+ }
+
+ 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);
+ }
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/hotspot/FallbackFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/FallbackFragment.java
index efc81a9f6..394012f8c 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/hotspot/FallbackFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/FallbackFragment.java
@@ -31,6 +31,7 @@ import static android.view.View.VISIBLE;
import static androidx.transition.TransitionManager.beginDelayedTransition;
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
import static org.briarproject.briar.android.hotspot.HotspotViewModel.getApkFileName;
+import static org.briarproject.briar.android.util.UiUtils.tryToStartActivity;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -102,7 +103,7 @@ public class FallbackFragment extends BaseFragment {
i.putExtra(EXTRA_STREAM, uri);
i.setType("*/*"); // gives us all sharing options
i.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
- startActivity(Intent.createChooser(i, null));
+ tryToStartActivity(requireActivity(), Intent.createChooser(i, null));
}
}
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..db8e1cf22 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
@@ -49,8 +49,10 @@ public class HotspotIntroFragment extends Fragment {
private TextView progressTextView;
private final AbstractConditionManager conditionManager = SDK_INT < 29 ?
- new ConditionManager(this, this::onPermissionUpdate) :
- new ConditionManager29(this, this::onPermissionUpdate);
+ new ConditionManager(this::onPermissionUpdate) :
+ SDK_INT >= 33 ?
+ new ConditionManager33(this, this::onPermissionUpdate) :
+ new ConditionManager29(this, this::onPermissionUpdate);
@Override
public void onAttach(Context context) {
@@ -87,7 +89,6 @@ public class HotspotIntroFragment extends Fragment {
}
private void onButtonClick(View view) {
- startButton.setEnabled(false);
startHotspotIfConditionsFulfilled();
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java
index 30d224c0a..7f5640444 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java
@@ -22,13 +22,19 @@ import org.briarproject.nullsafety.ParametersNotNullByDefault;
import javax.annotation.Nullable;
import javax.inject.Inject;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts.RequestPermission;
import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ViewModelProvider;
+import static android.Manifest.permission.POST_NOTIFICATIONS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Build.VERSION.SDK_INT;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
+import static androidx.core.content.ContextCompat.checkSelfPermission;
import static org.briarproject.bramble.api.crypto.DecryptionResult.KEY_STRENGTHENER_ERROR;
import static org.briarproject.bramble.api.crypto.DecryptionResult.SUCCESS;
import static org.briarproject.briar.android.login.LoginUtils.createKeyStrengthenerErrorDialog;
@@ -52,6 +58,10 @@ public class PasswordFragment extends BaseFragment implements TextWatcher {
private TextInputLayout input;
private TextInputEditText password;
+ private final ActivityResultLauncher requestPermissionLauncher =
+ registerForActivityResult(new RequestPermission(), isGranted ->
+ validatePassword());
+
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
@@ -109,6 +119,17 @@ public class PasswordFragment extends BaseFragment implements TextWatcher {
hideSoftKeyboard(password);
signInButton.setVisibility(INVISIBLE);
progress.setVisibility(VISIBLE);
+ if (SDK_INT >= 33 &&
+ checkSelfPermission(requireContext(), POST_NOTIFICATIONS) !=
+ PERMISSION_GRANTED) {
+ // this calls validatePassword() when it returns
+ requestPermissionLauncher.launch(POST_NOTIFICATIONS);
+ } else {
+ validatePassword();
+ }
+ }
+
+ private void validatePassword() {
viewModel.validatePassword(password.getText().toString());
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportFormFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportFormFragment.java
index 8eff6d704..b915b815a 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportFormFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportFormFragment.java
@@ -1,6 +1,5 @@
package org.briarproject.briar.android.reporting;
-import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@@ -33,13 +32,11 @@ import androidx.recyclerview.widget.RecyclerView;
import static android.view.View.GONE;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
-import static android.widget.Toast.LENGTH_LONG;
import static android.widget.Toast.LENGTH_SHORT;
import static java.util.Objects.requireNonNull;
-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.android.util.UiUtils.onSingleLinkClick;
+import static org.briarproject.briar.android.util.UiUtils.tryToStartActivity;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -180,13 +177,7 @@ public class ReportFormFragment extends BaseFragment {
private void triggerPrivacyPolicy() {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse("https://briarproject.org/privacy-policy/\\"));
- try {
- startActivity(i);
- } catch (ActivityNotFoundException e) {
- logException(LOG, WARNING, e);
- Toast.makeText(requireContext(),
- R.string.error_start_activity, LENGTH_LONG).show();
- }
+ tryToStartActivity(requireActivity(), i);
}
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/AboutFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/AboutFragment.java
index 664e7f286..fff46763e 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/settings/AboutFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/AboutFragment.java
@@ -1,6 +1,5 @@
package org.briarproject.briar.android.settings;
-import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@@ -8,7 +7,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-import android.widget.Toast;
import org.briarproject.briar.BuildConfig;
import org.briarproject.briar.R;
@@ -21,10 +19,9 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
-import static android.widget.Toast.LENGTH_LONG;
-import static java.util.logging.Level.WARNING;
+import static android.content.Intent.ACTION_VIEW;
import static java.util.logging.Logger.getLogger;
-import static org.briarproject.bramble.util.LogUtils.logException;
+import static org.briarproject.briar.android.util.UiUtils.tryToStartActivity;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -85,16 +82,9 @@ public class AboutFragment extends Fragment {
}
private void goToUrl(String url) {
- Intent i = new Intent(Intent.ACTION_VIEW);
+ Intent i = new Intent(ACTION_VIEW);
i.setData(Uri.parse(url));
- try {
- startActivity(i);
- } catch (ActivityNotFoundException e) {
- logException(LOG, WARNING, e);
- Toast.makeText(requireContext(),
- R.string.error_start_activity, LENGTH_LONG).show();
- }
-
+ tryToStartActivity(requireActivity(), i);
}
}
\ No newline at end of file
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java
index 2d5ec5fc1..314c283cb 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java
@@ -1,12 +1,10 @@
package org.briarproject.briar.android.settings;
-import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
-import android.widget.Toast;
import org.briarproject.briar.R;
import org.briarproject.briar.android.mailbox.MailboxActivity;
@@ -28,12 +26,12 @@ import androidx.preference.PreferenceGroup;
import static android.content.Intent.ACTION_SEND;
import static android.content.Intent.EXTRA_TEXT;
-import static android.widget.Toast.LENGTH_LONG;
import static java.util.Objects.requireNonNull;
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
import static org.briarproject.briar.android.util.UiUtils.launchActivityToOpenFile;
import static org.briarproject.briar.android.util.UiUtils.triggerFeedback;
+import static org.briarproject.briar.android.util.UiUtils.tryToStartActivity;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -101,12 +99,8 @@ public class SettingsFragment extends PreferenceFragmentCompat {
Intent sendIntent = new Intent(ACTION_SEND);
sendIntent.putExtra(EXTRA_TEXT, text);
sendIntent.setType("text/plain");
- try {
- startActivity(Intent.createChooser(sendIntent, null));
- } catch (ActivityNotFoundException e) {
- Toast.makeText(requireContext(),
- R.string.error_start_activity, LENGTH_LONG).show();
- }
+ tryToStartActivity(requireActivity(),
+ Intent.createChooser(sendIntent, null));
return true;
});
Preference prefFeedback =
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/PermissionUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/PermissionUtils.java
index fdad47615..d1c6432a8 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/util/PermissionUtils.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/util/PermissionUtils.java
@@ -1,12 +1,10 @@
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;
@@ -29,10 +27,10 @@ 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;
+import static org.briarproject.briar.android.util.UiUtils.tryToStartActivity;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -47,7 +45,7 @@ public class PermissionUtils {
}
}
- public static boolean isPermissionGranted(Context ctx, String permission) {
+ private static boolean isPermissionGranted(Context ctx, String permission) {
return checkSelfPermission(ctx, permission) ==
PERMISSION_GRANTED;
}
@@ -68,7 +66,7 @@ public class PermissionUtils {
gotPermission(ctx, grantedMap, BLUETOOTH_SCAN);
}
- public static DialogInterface.OnClickListener getGoToSettingsListener(
+ private static DialogInterface.OnClickListener getGoToSettingsListener(
Context context) {
return (dialog, which) -> {
Intent i = new Intent();
@@ -76,7 +74,7 @@ public class PermissionUtils {
i.addCategory(CATEGORY_DEFAULT);
i.setData(Uri.parse("package:" + APPLICATION_ID));
i.addFlags(FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(i);
+ tryToStartActivity(context, i);
};
}
@@ -123,12 +121,7 @@ public class PermissionUtils {
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();
- }
+ tryToStartActivity(ctx, i);
});
builder.show();
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java
index 78a0e3ad2..c92d5829e 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java
@@ -157,6 +157,15 @@ public class UiUtils {
ta.commit();
}
+ public static void tryToStartActivity(Context ctx, Intent intent) {
+ try {
+ ctx.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(ctx, R.string.error_start_activity, LENGTH_LONG)
+ .show();
+ }
+ }
+
public static String getContactDisplayName(Author author,
@Nullable String alias) {
String name = author.getName();
diff --git a/briar-android/src/main/res/layout/info_view.xml b/briar-android/src/main/res/layout/info_view.xml
index c7cf40f7b..8a884a4d8 100644
--- a/briar-android/src/main/res/layout/info_view.xml
+++ b/briar-android/src/main/res/layout/info_view.xml
@@ -13,10 +13,10 @@
android:layout_margin="@dimen/margin_medium"
android:contentDescription="@string/info"
android:drawablePadding="@dimen/margin_medium"
- android:drawableTint="?attr/colorControlNormal"
android:gravity="center_vertical"
app:drawableLeftCompat="@drawable/ic_info_dark"
app:drawableStartCompat="@drawable/ic_info_dark"
+ app:drawableTint="?attr/colorControlNormal"
tools:text="Did you know that if you took all the veins out of your body and laid them out end to end, you would die?" />
\ No newline at end of file
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.