From e409b315b424e08db7db3b412a15a034a644c1c9 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Thu, 12 May 2022 22:18:59 +0200 Subject: [PATCH] Refactor permission requests. Fixes #1224. --- .../opentracks/TrackRecordingActivity.java | 15 +---- .../sensors/BluetoothRemoteSensorManager.java | 4 ++ .../BluetoothLeSensorPreference.java | 28 +++------ .../opentracks/util/PermissionUtils.java | 60 ++++++++++++++++++- 4 files changed, 70 insertions(+), 37 deletions(-) diff --git a/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java b/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java index 5a09b512e..e045988c4 100644 --- a/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java +++ b/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java @@ -1,6 +1,5 @@ package de.dennisguse.opentracks; -import android.Manifest; import android.app.AlertDialog; import android.content.Context; import android.content.Intent; @@ -15,8 +14,6 @@ import android.view.View; import android.view.WindowManager; import android.widget.Toast; -import androidx.activity.result.ActivityResultLauncher; -import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; @@ -379,17 +376,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc return; } - ActivityResultLauncher locationPermissionRequest = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), result -> { - Boolean fineLocationGranted = result.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false); - Boolean coarseLocationGranted = result.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false); - if (fineLocationGranted == null || !fineLocationGranted - || coarseLocationGranted == null || !coarseLocationGranted) { - Toast.makeText(this, R.string.permission_gps_failed, Toast.LENGTH_SHORT).show(); - } - } - ); - String[] permissions = new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}; - locationPermissionRequest.launch(permissions); + PermissionUtils.requestGPSPermission(this, null, () -> Toast.makeText(this, R.string.permission_gps_failed, Toast.LENGTH_SHORT).show()); } private void onGpsStatusChanged(GpsStatusValue gpsStatusValue) { diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java index 7ce4684c0..e3bf79e93 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java @@ -35,6 +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; /** * Bluetooth LE sensor manager: manages connections to Bluetooth LE sensors. @@ -161,6 +162,9 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager. } else { connectionManager.disconnect(); } + if (!PermissionUtils.hasBluetoothPermissions(context)) { + Log.w(TAG, "BLUETOOTH_SCAN and/or BLUETOOTH_CONNECT not granted; not connecting."); + } Log.i(TAG, "Connecting to bluetooth address: " + address); try { 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 03936b230..eb75e5e11 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java @@ -1,6 +1,5 @@ package de.dennisguse.opentracks.settings.bluetooth; -import android.Manifest; import android.bluetooth.BluetoothAdapter; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; @@ -9,8 +8,6 @@ import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.content.Context; import android.content.DialogInterface; -import android.content.pm.PackageManager; -import android.os.Build; import android.os.Bundle; import android.os.ParcelUuid; import android.text.TextUtils; @@ -18,10 +15,7 @@ import android.util.AttributeSet; import android.util.Log; import android.widget.Toast; -import androidx.activity.result.ActivityResultLauncher; -import androidx.activity.result.contract.ActivityResultContracts; import androidx.appcompat.app.AlertDialog; -import androidx.core.content.ContextCompat; import androidx.preference.DialogPreference; import androidx.preference.PreferenceDialogFragmentCompat; import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat; @@ -35,6 +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; /** * Preference to select a discoverable Bluetooth LE device. @@ -150,26 +145,17 @@ public abstract class BluetoothLeSensorPreference extends DialogPreference { bluetoothIcon = AnimatedVectorDrawableCompat.create(getContext(), R.drawable.ic_bluetooth_searching_animated_24dp); bluetoothIcon.start(); - if ( - Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && ( - ContextCompat.checkSelfPermission(getContext(), Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED || - ContextCompat.checkSelfPermission(getContext(), Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED - ) - ) { - ActivityResultLauncher requestPermissionLauncher = - registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), granted -> { - if (!granted.containsValue(false)) { - startBluetoothScan(); - } else if (shouldShowRequestPermissionRationale(Manifest.permission.BLUETOOTH_SCAN) || shouldShowRequestPermissionRationale(Manifest.permission.BLUETOOTH_CONNECT)) { + 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(); - dismiss(); } else { Toast.makeText(getContext(), R.string.permission_bluetooth_failed, Toast.LENGTH_SHORT).show(); - dismiss(); } + dismiss(); }); - - requestPermissionLauncher.launch(new String[]{Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH_SCAN}); } else { startBluetoothScan(); } diff --git a/src/main/java/de/dennisguse/opentracks/util/PermissionUtils.java b/src/main/java/de/dennisguse/opentracks/util/PermissionUtils.java index b2ffb37aa..044dfe07b 100644 --- a/src/main/java/de/dennisguse/opentracks/util/PermissionUtils.java +++ b/src/main/java/de/dennisguse/opentracks/util/PermissionUtils.java @@ -3,16 +3,72 @@ 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 ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED - && ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED; + 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); } }