diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index cce83ac87..faa9c06a8 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -59,6 +59,8 @@ limitations under the License.
tools:targetApi="s" />
+
+
Toast.makeText(this, R.string.permission_gps_failed, Toast.LENGTH_SHORT).show());
- }
-
- if (!PermissionUtils.hasBluetoothPermissions(this)) {
- PermissionUtils.requestBluetoothPermission(this, null, null);
- }
+ PermissionRequester.ALL.requestPermissionsIfNeeded(this, this, null, (requester) -> Toast.makeText(this, R.string.permission_gps_failed, Toast.LENGTH_SHORT).show());
}
@Override
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java
index e3bf79e93..a5c5aa388 100644
--- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java
+++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java
@@ -35,7 +35,7 @@ import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
import de.dennisguse.opentracks.settings.PreferencesUtils;
-import de.dennisguse.opentracks.util.PermissionUtils;
+import de.dennisguse.opentracks.util.PermissionRequester;
/**
* Bluetooth LE sensor manager: manages connections to Bluetooth LE sensors.
@@ -162,7 +162,7 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
} else {
connectionManager.disconnect();
}
- if (!PermissionUtils.hasBluetoothPermissions(context)) {
+ if (!PermissionRequester.BLUETOOTH.hasPermission(context)) {
Log.w(TAG, "BLUETOOTH_SCAN and/or BLUETOOTH_CONNECT not granted; not connecting.");
}
diff --git a/src/main/java/de/dennisguse/opentracks/services/handlers/GPSHandler.java b/src/main/java/de/dennisguse/opentracks/services/handlers/GPSHandler.java
index abf6be108..88a15f4b4 100644
--- a/src/main/java/de/dennisguse/opentracks/services/handlers/GPSHandler.java
+++ b/src/main/java/de/dennisguse/opentracks/services/handlers/GPSHandler.java
@@ -19,7 +19,7 @@ import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.util.LocationUtils;
-import de.dennisguse.opentracks.util.PermissionUtils;
+import de.dennisguse.opentracks.util.PermissionRequester;
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public class GPSHandler implements LocationListener, GpsStatus.GpsStatusListener, SharedPreferences.OnSharedPreferenceChangeListener {
@@ -54,7 +54,7 @@ public class GPSHandler implements LocationListener, GpsStatus.GpsStatusListener
//TODO upgrade to AGP7.0.0/API31 started complaining about removeUpdates.
public void onStop() {
if (locationManager != null && context != null) {
- if (PermissionUtils.hasGPSPermission(context)) {
+ if (PermissionRequester.GPS.hasPermission(context)) {
locationManager.removeUpdates(this);
}
locationManager = null;
@@ -152,7 +152,7 @@ public class GPSHandler implements LocationListener, GpsStatus.GpsStatusListener
Log.e(TAG, "Not started.");
return;
}
- if (PermissionUtils.hasGPSPermission(context)) {
+ if (PermissionRequester.GPS.hasPermission(context)) {
try {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, gpsInterval.toMillis(), 0, this);
} catch (SecurityException e) {
diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java
index eb75e5e11..c3d759eb1 100644
--- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java
+++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java
@@ -29,7 +29,7 @@ import java.util.stream.Collectors;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.sensors.BluetoothUtils;
import de.dennisguse.opentracks.settings.PreferencesUtils;
-import de.dennisguse.opentracks.util.PermissionUtils;
+import de.dennisguse.opentracks.util.PermissionRequester;
/**
* Preference to select a discoverable Bluetooth LE device.
@@ -145,20 +145,18 @@ public abstract class BluetoothLeSensorPreference extends DialogPreference {
bluetoothIcon = AnimatedVectorDrawableCompat.create(getContext(), R.drawable.ic_bluetooth_searching_animated_24dp);
bluetoothIcon.start();
- if (!PermissionUtils.hasBluetoothPermissions(getContext())) {
- PermissionUtils.requestBluetoothPermission(this,
- this::startBluetoothScan,
- () -> {
- if (PermissionUtils.shouldShowRequestPermissionRationaleBluetooth(this)) {
- Toast.makeText(getContext(), R.string.permission_bluetooth_failed_rejected, Toast.LENGTH_LONG).show();
- } else {
- Toast.makeText(getContext(), R.string.permission_bluetooth_failed, Toast.LENGTH_SHORT).show();
- }
- dismiss();
- });
- } else {
- startBluetoothScan();
- }
+ PermissionRequester.BLUETOOTH.requestPermissionsIfNeeded(getContext(), this,
+ this::startBluetoothScan,
+ (requester) -> {
+ if (requester.shouldShowRequestPermissionRationale(this)) {
+ Toast.makeText(getContext(), R.string.permission_bluetooth_failed_rejected, Toast.LENGTH_LONG).show();
+ } else {
+ Toast.makeText(getContext(), R.string.permission_bluetooth_failed, Toast.LENGTH_SHORT).show();
+ }
+ dismiss();
+ });
+
+ startBluetoothScan();
}
private void startBluetoothScan() {
diff --git a/src/main/java/de/dennisguse/opentracks/util/PermissionRequester.java b/src/main/java/de/dennisguse/opentracks/util/PermissionRequester.java
new file mode 100644
index 000000000..031b7ab47
--- /dev/null
+++ b/src/main/java/de/dennisguse/opentracks/util/PermissionRequester.java
@@ -0,0 +1,101 @@
+package de.dennisguse.opentracks.util;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build;
+
+import androidx.activity.result.ActivityResultCaller;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.annotation.Nullable;
+import androidx.core.content.ContextCompat;
+import androidx.fragment.app.Fragment;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class PermissionRequester {
+
+ private final List permissions;
+
+ public PermissionRequester(List permissions) {
+ this.permissions = permissions;
+ }
+
+ public boolean hasPermission(Context context) {
+ return permissions.stream()
+ .map(p -> ContextCompat.checkSelfPermission(context, p))
+ .allMatch(r -> r == PackageManager.PERMISSION_GRANTED);
+ }
+
+ public void requestPermissionsIfNeeded(Context context, ActivityResultCaller caller, @Nullable Runnable onGranted, @Nullable RejectedCallback onRejected) {
+ if (!hasPermission(context)) {
+ requestPermission(caller, onGranted, onRejected);
+ }
+ }
+
+ public boolean shouldShowRequestPermissionRationale(Fragment context) {
+ return permissions.stream()
+ .anyMatch(context::shouldShowRequestPermissionRationale);
+ }
+
+ private void requestPermission(ActivityResultCaller context, @Nullable Runnable onGranted, @Nullable RejectedCallback onRejected) {
+ ActivityResultLauncher locationPermissionRequest = context.registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), result -> {
+ boolean isGranted = permissions.stream()
+ .allMatch(p -> result.getOrDefault(p, false));
+ if (isGranted && onGranted != null) {
+ onGranted.run();
+ }
+ if (!isGranted && onRejected != null) {
+ onRejected.rejected(this);
+ }
+ }
+ );
+
+ locationPermissionRequest.launch(permissions.toArray(permissions.toArray(new String[0])));
+ }
+
+ private static final List GPS_PERMISSION = List.of(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION);
+
+ private static final List BLUETOOTH_PERMISSIONS;
+
+ static {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ BLUETOOTH_PERMISSIONS = List.of(Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT);
+ } else {
+ BLUETOOTH_PERMISSIONS = Collections.emptyList();
+ }
+ }
+
+ private static final List NOTIFICATION_PERMISSIONS;
+
+ static {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ NOTIFICATION_PERMISSIONS = List.of(Manifest.permission.POST_NOTIFICATIONS);
+ } else {
+ NOTIFICATION_PERMISSIONS = Collections.emptyList();
+ }
+ }
+
+ private static final List ALL_PERMISSIONS;
+
+ static {
+ ArrayList all = new ArrayList<>(GPS_PERMISSION);
+ all.addAll(BLUETOOTH_PERMISSIONS);
+ all.addAll(NOTIFICATION_PERMISSIONS);
+
+ ALL_PERMISSIONS = Collections.unmodifiableList(all);
+ }
+
+ public final static PermissionRequester GPS = new PermissionRequester(GPS_PERMISSION);
+ public final static PermissionRequester BLUETOOTH = new PermissionRequester(BLUETOOTH_PERMISSIONS);
+ public final static PermissionRequester NOTIFICATION = new PermissionRequester(NOTIFICATION_PERMISSIONS);
+
+ public final static PermissionRequester ALL = new PermissionRequester(ALL_PERMISSIONS);
+
+ public interface RejectedCallback {
+ void rejected(PermissionRequester permissionRequester);
+ }
+}
diff --git a/src/main/java/de/dennisguse/opentracks/util/PermissionUtils.java b/src/main/java/de/dennisguse/opentracks/util/PermissionUtils.java
deleted file mode 100644
index 044dfe07b..000000000
--- a/src/main/java/de/dennisguse/opentracks/util/PermissionUtils.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package de.dennisguse.opentracks.util;
-
-import android.Manifest;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.Build;
-
-import androidx.activity.result.ActivityResultCaller;
-import androidx.activity.result.ActivityResultLauncher;
-import androidx.activity.result.contract.ActivityResultContracts;
-import androidx.annotation.Nullable;
-import androidx.core.content.ContextCompat;
-import androidx.fragment.app.Fragment;
-
-import java.util.Arrays;
-
-public class PermissionUtils {
-
- private static final String[] GPS = new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};
- private static final String[] BLUETOOTH;
-
- static {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
- BLUETOOTH = new String[]{};
- } else {
- BLUETOOTH = new String[]{Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT};
- }
- }
-
- private PermissionUtils() {
- }
-
- public static boolean hasGPSPermission(Context context) {
- return hasPermissions(context, GPS);
- }
-
- public static void requestGPSPermission(ActivityResultCaller context, @Nullable Runnable onGranted, @Nullable Runnable onRejected) {
- requestPermission(context, GPS, onGranted, onRejected);
- }
-
- public static boolean shouldShowRequestPermissionRationaleBluetooth(Fragment context) {
- return Arrays.stream(BLUETOOTH).anyMatch(context::shouldShowRequestPermissionRationale);
- }
-
- public static boolean hasBluetoothPermissions(Context context) {
- return hasPermissions(context, BLUETOOTH);
- }
-
- public static void requestBluetoothPermission(ActivityResultCaller context, @Nullable Runnable onGranted, @Nullable Runnable onRejected) {
- requestPermission(context, BLUETOOTH, onGranted, onRejected);
- }
-
- private static void requestPermission(ActivityResultCaller context, final String[] permissions, @Nullable Runnable onGranted, @Nullable Runnable onRejected) {
- ActivityResultLauncher locationPermissionRequest = context.registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), result -> {
- boolean isGranted = Arrays.stream(permissions)
- .allMatch(p -> result.getOrDefault(p, false));
- if (isGranted && onGranted != null) {
- onGranted.run();
- }
- if (!isGranted && onRejected != null) {
- onRejected.run();
- }
- }
- );
-
- locationPermissionRequest.launch(permissions);
- }
-
- private static boolean hasPermissions(Context context, String[] permissions) {
- return Arrays.stream(permissions)
- .map(p -> ContextCompat.checkSelfPermission(context, p))
- .allMatch(r -> r == PackageManager.PERMISSION_GRANTED);
- }
-}